aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Unit 193 <unit193@ubuntu.com>2018-04-25 18:07:30 -0400
committerLibravatar Unit 193 <unit193@ubuntu.com>2018-04-25 18:07:30 -0400
commit9b1b081cfdb1c0fb6457278775e0823f8bc10f62 (patch)
treece8840148d8445055ba9e4f12263b2208f234c16
downloadbarrier-9b1b081cfdb1c0fb6457278775e0823f8bc10f62.tar.bz2
barrier-9b1b081cfdb1c0fb6457278775e0823f8bc10f62.tar.xz
barrier-9b1b081cfdb1c0fb6457278775e0823f8bc10f62.tar.zst
Import Upstream version 2.0.0+dfsgupstream/2.0.0+dfsg
-rw-r--r--.github/ISSUE_TEMPLATE.md30
-rw-r--r--.gitignore22
-rw-r--r--.travis.yml10
-rw-r--r--Build.properties7
-rw-r--r--CMakeLists.txt395
-rw-r--r--ChangeLog426
-rw-r--r--LICENSE288
-rw-r--r--README.md31
-rw-r--r--build_installer.bat35
-rw-r--r--clean_build.bat71
-rwxr-xr-xclean_build.sh28
-rw-r--r--cmake/Package.cmake9
-rw-r--r--cmake/Version.cmake94
-rw-r--r--debian/compat1
-rw-r--r--debian/control17
-rw-r--r--debian/copyright5
-rwxr-xr-xdebian/rules5
-rw-r--r--debian/source/format1
-rw-r--r--dist/inno/barrier.iss.in74
-rw-r--r--dist/inno/scripts/isxdl/english.ini49
-rw-r--r--dist/inno/scripts/isxdl/isxdl.dllbin0 -> 124416 bytes
-rw-r--r--dist/inno/scripts/isxdl/isxdl.iss14
-rw-r--r--dist/inno/scripts/lang/english.iss18
-rw-r--r--dist/inno/scripts/products.iss6
-rw-r--r--dist/inno/scripts/products.pas329
-rw-r--r--dist/inno/scripts/products/msiproduct.iss49
-rw-r--r--dist/inno/scripts/products/stringversion.iss62
-rw-r--r--dist/inno/scripts/products/vcredist2017.iss32
-rw-r--r--dist/inno/scripts/products/winversion.iss49
-rw-r--r--dist/macos/bundle/Barrier.app/Contents/Info.plist.in32
-rw-r--r--dist/macos/bundle/Barrier.app/Contents/PkgInfo1
-rw-r--r--dist/macos/bundle/Barrier.app/Contents/Resources/Barrier.icnsbin0 -> 470437 bytes
-rwxr-xr-xdist/macos/bundle/build_installer.sh.in77
-rwxr-xr-xdist/macos/bundle/reref_dylibs.sh41
-rw-r--r--dist/rpm/barrier.spec.in27
-rw-r--r--dist/wix/Barrier.sln27
-rw-r--r--dist/wix/Barrier.wixproj31
-rw-r--r--dist/wix/Include.wxi.in25
-rw-r--r--dist/wix/Product.wxs106
-rw-r--r--doc/MacReadme.txt15
-rw-r--r--doc/QtCodeStyle.xml234
-rw-r--r--doc/UpdateManpages.txt5
-rw-r--r--doc/barrier.conf.example37
-rw-r--r--doc/barrier.conf.example-advanced55
-rw-r--r--doc/barrier.conf.example-basic39
-rw-r--r--doc/barrierc.172
-rw-r--r--doc/barriers.181
-rw-r--r--doc/org.barrier-foss.org.barrierc.plist20
-rw-r--r--doc/org.barrier-foss.org.barriers.plist22
-rw-r--r--osx_environment.sh17
-rw-r--r--res/License.rtf102
-rw-r--r--res/License.tex422
-rw-r--r--res/Readme.txt9
-rw-r--r--res/banner.bmpbin0 -> 114514 bytes
-rw-r--r--res/barrier.desktop12
-rw-r--r--res/barrier.icobin0 -> 121502 bytes
-rw-r--r--res/barrier.pngbin0 -> 19498 bytes
-rw-r--r--res/barrier.svg112
-rw-r--r--res/barrier2.desktop11
-rw-r--r--res/config.h.in173
-rw-r--r--res/dialog.bmpbin0 -> 615402 bytes
-rw-r--r--res/doxygen.cfg.in1635
-rwxr-xr-xres/makeicon.sh22
-rw-r--r--res/openssl/barrier.conf65
-rw-r--r--src/CMakeLists.txt25
-rw-r--r--src/cmd/CMakeLists.txt24
-rw-r--r--src/cmd/barrierc/.gitignore1
-rw-r--r--src/cmd/barrierc/CMakeLists.txt57
-rw-r--r--src/cmd/barrierc/MSWindowsClientTaskBarReceiver.cpp376
-rw-r--r--src/cmd/barrierc/MSWindowsClientTaskBarReceiver.h68
-rw-r--r--src/cmd/barrierc/OSXClientTaskBarReceiver.cpp69
-rw-r--r--src/cmd/barrierc/OSXClientTaskBarReceiver.h37
-rw-r--r--src/cmd/barrierc/XWindowsClientTaskBarReceiver.cpp68
-rw-r--r--src/cmd/barrierc/XWindowsClientTaskBarReceiver.h38
-rw-r--r--src/cmd/barrierc/barrierc.cpp58
-rw-r--r--src/cmd/barrierc/barrierc.icobin0 -> 121502 bytes
-rw-r--r--src/cmd/barrierc/barrierc.rc141
-rw-r--r--src/cmd/barrierc/resource.h37
-rw-r--r--src/cmd/barrierc/tb_error.icobin0 -> 318 bytes
-rw-r--r--src/cmd/barrierc/tb_idle.icobin0 -> 318 bytes
-rw-r--r--src/cmd/barrierc/tb_run.icobin0 -> 318 bytes
-rw-r--r--src/cmd/barrierc/tb_wait.icobin0 -> 318 bytes
-rw-r--r--src/cmd/barrierd/CMakeLists.txt27
-rw-r--r--src/cmd/barrierd/barrierd.cpp44
-rw-r--r--src/cmd/barriers/.gitignore1
-rw-r--r--src/cmd/barriers/CMakeLists.txt58
-rw-r--r--src/cmd/barriers/MSWindowsServerTaskBarReceiver.cpp408
-rw-r--r--src/cmd/barriers/MSWindowsServerTaskBarReceiver.h69
-rw-r--r--src/cmd/barriers/OSXServerTaskBarReceiver.cpp67
-rw-r--r--src/cmd/barriers/OSXServerTaskBarReceiver.h36
-rw-r--r--src/cmd/barriers/XWindowsServerTaskBarReceiver.cpp67
-rw-r--r--src/cmd/barriers/XWindowsServerTaskBarReceiver.h38
-rw-r--r--src/cmd/barriers/barriers.cpp57
-rw-r--r--src/cmd/barriers/barriers.icobin0 -> 121502 bytes
-rw-r--r--src/cmd/barriers/barriers.rc134
-rw-r--r--src/cmd/barriers/resource.h42
-rw-r--r--src/cmd/barriers/tb_error.icobin0 -> 318 bytes
-rw-r--r--src/cmd/barriers/tb_idle.icobin0 -> 318 bytes
-rw-r--r--src/cmd/barriers/tb_run.icobin0 -> 318 bytes
-rw-r--r--src/cmd/barriers/tb_wait.icobin0 -> 318 bytes
-rw-r--r--src/cmd/syntool/CMakeLists.txt27
-rw-r--r--src/cmd/syntool/syntool.cpp31
-rw-r--r--src/gui/CMakeLists.txt52
-rw-r--r--src/gui/gui.pro162
-rw-r--r--src/gui/gui.ts1407
-rw-r--r--src/gui/lang.cmd1
-rw-r--r--src/gui/langbuild.cmd2
-rw-r--r--src/gui/res/Barrier.qrc58
-rw-r--r--src/gui/res/icons/16x16/barrier-connected.pngbin0 -> 1054 bytes
-rw-r--r--src/gui/res/icons/16x16/barrier-connected.xcfbin0 -> 8583 bytes
-rw-r--r--src/gui/res/icons/16x16/barrier-disconnected.pngbin0 -> 946 bytes
-rw-r--r--src/gui/res/icons/16x16/barrier-disconnected.xcfbin0 -> 7281 bytes
-rw-r--r--src/gui/res/icons/16x16/barrier-transfering.pngbin0 -> 813 bytes
-rw-r--r--src/gui/res/icons/16x16/barrier-transfering.xcfbin0 -> 2367 bytes
-rw-r--r--src/gui/res/icons/16x16/money.pngbin0 -> 738 bytes
-rw-r--r--src/gui/res/icons/16x16/padlock.pngbin0 -> 450 bytes
-rw-r--r--src/gui/res/icons/16x16/warning.pngbin0 -> 693 bytes
-rw-r--r--src/gui/res/icons/256x256/barrier.icobin0 -> 121502 bytes
-rw-r--r--src/gui/res/icons/64x64/user-trash.pngbin0 -> 3815 bytes
-rw-r--r--src/gui/res/icons/64x64/video-display.pngbin0 -> 2579 bytes
-rw-r--r--src/gui/res/image/about.pngbin0 -> 3563 bytes
-rw-r--r--src/gui/res/image/spinning-wheel.gifbin0 -> 9689 bytes
-rw-r--r--src/gui/res/lang/Languages.xml46
-rw-r--r--src/gui/res/lang/gui_af-ZA.qm1
-rw-r--r--src/gui/res/lang/gui_af-ZA.ts1405
-rw-r--r--src/gui/res/lang/gui_ar.qmbin0 -> 7606 bytes
-rw-r--r--src/gui/res/lang/gui_ar.ts1405
-rw-r--r--src/gui/res/lang/gui_bg-BG.qmbin0 -> 21881 bytes
-rw-r--r--src/gui/res/lang/gui_bg-BG.ts1410
-rw-r--r--src/gui/res/lang/gui_ca-AD.qmbin0 -> 23075 bytes
-rw-r--r--src/gui/res/lang/gui_ca-AD.ts1411
-rw-r--r--src/gui/res/lang/gui_cs-CZ.qmbin0 -> 22885 bytes
-rw-r--r--src/gui/res/lang/gui_cs-CZ.ts1411
-rw-r--r--src/gui/res/lang/gui_cy.qmbin0 -> 15069 bytes
-rw-r--r--src/gui/res/lang/gui_cy.ts1407
-rw-r--r--src/gui/res/lang/gui_da.qmbin0 -> 22064 bytes
-rw-r--r--src/gui/res/lang/gui_da.ts1410
-rw-r--r--src/gui/res/lang/gui_de.qmbin0 -> 23630 bytes
-rw-r--r--src/gui/res/lang/gui_de.ts1411
-rw-r--r--src/gui/res/lang/gui_es.qmbin0 -> 23608 bytes
-rw-r--r--src/gui/res/lang/gui_es.ts1411
-rw-r--r--src/gui/res/lang/gui_et-EE.qmbin0 -> 21523 bytes
-rw-r--r--src/gui/res/lang/gui_et-EE.ts1411
-rw-r--r--src/gui/res/lang/gui_fi.qmbin0 -> 21784 bytes
-rw-r--r--src/gui/res/lang/gui_fi.ts1411
-rw-r--r--src/gui/res/lang/gui_fr.qmbin0 -> 23634 bytes
-rw-r--r--src/gui/res/lang/gui_fr.ts1411
-rw-r--r--src/gui/res/lang/gui_gl.qmbin0 -> 3447 bytes
-rw-r--r--src/gui/res/lang/gui_gl.ts1405
-rw-r--r--src/gui/res/lang/gui_grk.qmbin0 -> 4629 bytes
-rw-r--r--src/gui/res/lang/gui_grk.ts1405
-rw-r--r--src/gui/res/lang/gui_he.qmbin0 -> 13575 bytes
-rw-r--r--src/gui/res/lang/gui_he.ts1405
-rw-r--r--src/gui/res/lang/gui_hi.qmbin0 -> 23 bytes
-rw-r--r--src/gui/res/lang/gui_hi.ts1405
-rw-r--r--src/gui/res/lang/gui_hr-HR.qmbin0 -> 20485 bytes
-rw-r--r--src/gui/res/lang/gui_hr-HR.ts1408
-rw-r--r--src/gui/res/lang/gui_hu-HU.qmbin0 -> 18573 bytes
-rw-r--r--src/gui/res/lang/gui_hu-HU.ts1407
-rw-r--r--src/gui/res/lang/gui_id.qmbin0 -> 4680 bytes
-rw-r--r--src/gui/res/lang/gui_id.ts1405
-rw-r--r--src/gui/res/lang/gui_is-IS.qm1
-rw-r--r--src/gui/res/lang/gui_is-IS.ts1405
-rw-r--r--src/gui/res/lang/gui_it.qmbin0 -> 21855 bytes
-rw-r--r--src/gui/res/lang/gui_it.ts1408
-rw-r--r--src/gui/res/lang/gui_ja-JP.qmbin0 -> 17379 bytes
-rw-r--r--src/gui/res/lang/gui_ja-JP.ts1411
-rw-r--r--src/gui/res/lang/gui_ko.qmbin0 -> 18395 bytes
-rw-r--r--src/gui/res/lang/gui_ko.ts1411
-rw-r--r--src/gui/res/lang/gui_lt.qmbin0 -> 2228 bytes
-rw-r--r--src/gui/res/lang/gui_lt.ts1405
-rw-r--r--src/gui/res/lang/gui_lv.qmbin0 -> 1234 bytes
-rw-r--r--src/gui/res/lang/gui_lv.ts1405
-rw-r--r--src/gui/res/lang/gui_mr.qmbin0 -> 3637 bytes
-rw-r--r--src/gui/res/lang/gui_mr.ts1405
-rw-r--r--src/gui/res/lang/gui_nl-NL.qmbin0 -> 22477 bytes
-rw-r--r--src/gui/res/lang/gui_nl-NL.ts1410
-rw-r--r--src/gui/res/lang/gui_no.qmbin0 -> 21716 bytes
-rw-r--r--src/gui/res/lang/gui_no.ts1412
-rw-r--r--src/gui/res/lang/gui_pes-IR.qmbin0 -> 1070 bytes
-rw-r--r--src/gui/res/lang/gui_pes-IR.ts1405
-rw-r--r--src/gui/res/lang/gui_pl-PL.qmbin0 -> 22465 bytes
-rw-r--r--src/gui/res/lang/gui_pl-PL.ts1411
-rw-r--r--src/gui/res/lang/gui_pt-BR.qmbin0 -> 22443 bytes
-rw-r--r--src/gui/res/lang/gui_pt-BR.ts1411
-rw-r--r--src/gui/res/lang/gui_pt-PT.qmbin0 -> 21258 bytes
-rw-r--r--src/gui/res/lang/gui_pt-PT.ts1407
-rw-r--r--src/gui/res/lang/gui_ro.qmbin0 -> 19806 bytes
-rw-r--r--src/gui/res/lang/gui_ro.ts1407
-rw-r--r--src/gui/res/lang/gui_ru.qmbin0 -> 22329 bytes
-rw-r--r--src/gui/res/lang/gui_ru.ts1414
-rw-r--r--src/gui/res/lang/gui_si.qmbin0 -> 677 bytes
-rw-r--r--src/gui/res/lang/gui_si.ts1405
-rw-r--r--src/gui/res/lang/gui_sk-SK.qmbin0 -> 1187 bytes
-rw-r--r--src/gui/res/lang/gui_sk-SK.ts1405
-rw-r--r--src/gui/res/lang/gui_sl-SI.qmbin0 -> 1709 bytes
-rw-r--r--src/gui/res/lang/gui_sl-SI.ts1405
-rw-r--r--src/gui/res/lang/gui_sq-AL.qmbin0 -> 20251 bytes
-rw-r--r--src/gui/res/lang/gui_sq-AL.ts1408
-rw-r--r--src/gui/res/lang/gui_sr.qmbin0 -> 117 bytes
-rw-r--r--src/gui/res/lang/gui_sr.ts1405
-rw-r--r--src/gui/res/lang/gui_sv.qmbin0 -> 22156 bytes
-rw-r--r--src/gui/res/lang/gui_sv.ts1411
-rw-r--r--src/gui/res/lang/gui_th-TH.qmbin0 -> 3721 bytes
-rw-r--r--src/gui/res/lang/gui_th-TH.ts1405
-rw-r--r--src/gui/res/lang/gui_tr-TR.qmbin0 -> 20445 bytes
-rw-r--r--src/gui/res/lang/gui_tr-TR.ts1410
-rw-r--r--src/gui/res/lang/gui_uk.qmbin0 -> 22693 bytes
-rw-r--r--src/gui/res/lang/gui_uk.ts1411
-rw-r--r--src/gui/res/lang/gui_ur.qmbin0 -> 1316 bytes
-rw-r--r--src/gui/res/lang/gui_ur.ts1405
-rw-r--r--src/gui/res/lang/gui_vi.qmbin0 -> 5101 bytes
-rw-r--r--src/gui/res/lang/gui_vi.ts1405
-rw-r--r--src/gui/res/lang/gui_zh-CN.qmbin0 -> 16197 bytes
-rw-r--r--src/gui/res/lang/gui_zh-CN.ts1411
-rw-r--r--src/gui/res/lang/gui_zh-TW.qmbin0 -> 16331 bytes
-rw-r--r--src/gui/res/lang/gui_zh-TW.ts1414
-rw-r--r--src/gui/res/mac/Info.plist28
-rw-r--r--src/gui/res/mac/QBarrier.icnsbin0 -> 124558 bytes
-rw-r--r--src/gui/res/win/Barrier.rc1
-rw-r--r--src/gui/src/AboutDialog.cpp55
-rw-r--r--src/gui/src/AboutDialog.h43
-rw-r--r--src/gui/src/AboutDialogBase.ui223
-rw-r--r--src/gui/src/Action.cpp150
-rw-r--r--src/gui/src/Action.h89
-rw-r--r--src/gui/src/ActionDialog.cpp109
-rw-r--r--src/gui/src/ActionDialog.h56
-rw-r--r--src/gui/src/ActionDialogBase.ui581
-rw-r--r--src/gui/src/AddClientDialog.cpp129
-rw-r--r--src/gui/src/AddClientDialog.h68
-rw-r--r--src/gui/src/AddClientDialogBase.ui144
-rw-r--r--src/gui/src/AppConfig.cpp231
-rw-r--r--src/gui/src/AppConfig.h140
-rw-r--r--src/gui/src/BarrierLocale.cpp68
-rw-r--r--src/gui/src/BarrierLocale.h48
-rw-r--r--src/gui/src/BaseConfig.cpp46
-rw-r--r--src/gui/src/BaseConfig.h91
-rw-r--r--src/gui/src/CommandProcess.cpp63
-rw-r--r--src/gui/src/CommandProcess.h43
-rw-r--r--src/gui/src/CoreInterface.cpp96
-rw-r--r--src/gui/src/CoreInterface.h36
-rw-r--r--src/gui/src/DataDownloader.cpp58
-rw-r--r--src/gui/src/DataDownloader.h53
-rw-r--r--src/gui/src/DisplayIsValid.cpp14
-rw-r--r--src/gui/src/DisplayIsValid.h5
-rw-r--r--src/gui/src/ElevateMode.h41
-rw-r--r--src/gui/src/Fingerprint.cpp149
-rw-r--r--src/gui/src/Fingerprint.h46
-rw-r--r--src/gui/src/Hotkey.cpp75
-rw-r--r--src/gui/src/Hotkey.h66
-rw-r--r--src/gui/src/HotkeyDialog.cpp41
-rw-r--r--src/gui/src/HotkeyDialog.h49
-rw-r--r--src/gui/src/HotkeyDialogBase.ui81
-rw-r--r--src/gui/src/Ipc.cpp26
-rw-r--r--src/gui/src/Ipc.h42
-rw-r--r--src/gui/src/IpcClient.cpp146
-rw-r--r--src/gui/src/IpcClient.h63
-rw-r--r--src/gui/src/IpcReader.cpp140
-rw-r--r--src/gui/src/IpcReader.h49
-rw-r--r--src/gui/src/KeySequence.cpp237
-rw-r--r--src/gui/src/KeySequence.h58
-rw-r--r--src/gui/src/KeySequenceWidget.cpp145
-rw-r--r--src/gui/src/KeySequenceWidget.h81
-rw-r--r--src/gui/src/LogWindow.cpp72
-rw-r--r--src/gui/src/LogWindow.h46
-rw-r--r--src/gui/src/LogWindowBase.ui86
-rw-r--r--src/gui/src/MainWindow.cpp1279
-rw-r--r--src/gui/src/MainWindow.h215
-rw-r--r--src/gui/src/MainWindowBase.ui470
-rw-r--r--src/gui/src/NewScreenWidget.cpp48
-rw-r--r--src/gui/src/NewScreenWidget.h40
-rw-r--r--src/gui/src/ProcessorArch.h28
-rw-r--r--src/gui/src/QBarrierApplication.cpp72
-rw-r--r--src/gui/src/QBarrierApplication.h47
-rw-r--r--src/gui/src/QUtility.cpp115
-rw-r--r--src/gui/src/QUtility.h31
-rw-r--r--src/gui/src/Screen.cpp147
-rw-r--r--src/gui/src/Screen.h105
-rw-r--r--src/gui/src/ScreenSettingsDialog.cpp137
-rw-r--r--src/gui/src/ScreenSettingsDialog.h53
-rw-r--r--src/gui/src/ScreenSettingsDialogBase.ui543
-rw-r--r--src/gui/src/ScreenSetupModel.cpp143
-rw-r--r--src/gui/src/ScreenSetupModel.h71
-rw-r--r--src/gui/src/ScreenSetupView.cpp161
-rw-r--r--src/gui/src/ScreenSetupView.h57
-rw-r--r--src/gui/src/ServerConfig.cpp403
-rw-r--r--src/gui/src/ServerConfig.h141
-rw-r--r--src/gui/src/ServerConfigDialog.cpp219
-rw-r--r--src/gui/src/ServerConfigDialog.h66
-rw-r--r--src/gui/src/ServerConfigDialogBase.ui781
-rw-r--r--src/gui/src/SettingsDialog.cpp140
-rw-r--r--src/gui/src/SettingsDialog.h54
-rw-r--r--src/gui/src/SettingsDialogBase.ui368
-rw-r--r--src/gui/src/SetupWizard.cpp149
-rw-r--r--src/gui/src/SetupWizard.h53
-rw-r--r--src/gui/src/SetupWizardBase.ui245
-rw-r--r--src/gui/src/ShutdownCh.h22
-rw-r--r--src/gui/src/SslCertificate.cpp178
-rw-r--r--src/gui/src/SslCertificate.h47
-rw-r--r--src/gui/src/TrashScreenWidget.cpp43
-rw-r--r--src/gui/src/TrashScreenWidget.h42
-rw-r--r--src/gui/src/VersionChecker.cpp111
-rw-r--r--src/gui/src/VersionChecker.h43
-rw-r--r--src/gui/src/WebClient.cpp83
-rw-r--r--src/gui/src/WebClient.h49
-rw-r--r--src/gui/src/ZeroconfBrowser.cpp92
-rw-r--r--src/gui/src/ZeroconfBrowser.h57
-rw-r--r--src/gui/src/ZeroconfRecord.h50
-rw-r--r--src/gui/src/ZeroconfRegister.cpp94
-rw-r--r--src/gui/src/ZeroconfRegister.h61
-rw-r--r--src/gui/src/ZeroconfServer.cpp33
-rw-r--r--src/gui/src/ZeroconfServer.h37
-rw-r--r--src/gui/src/ZeroconfService.cpp188
-rw-r--r--src/gui/src/ZeroconfService.h57
-rw-r--r--src/gui/src/ZeroconfThread.cpp38
-rw-r--r--src/gui/src/ZeroconfThread.h38
-rw-r--r--src/gui/src/main.cpp180
-rw-r--r--src/lib/CMakeLists.txt27
-rw-r--r--src/lib/arch/Arch.cpp60
-rw-r--r--src/lib/arch/Arch.h144
-rw-r--r--src/lib/arch/ArchConsoleStd.cpp33
-rw-r--r--src/lib/arch/ArchConsoleStd.h34
-rw-r--r--src/lib/arch/ArchDaemonNone.cpp85
-rw-r--r--src/lib/arch/ArchDaemonNone.h50
-rw-r--r--src/lib/arch/CMakeLists.txt47
-rw-r--r--src/lib/arch/IArchConsole.h66
-rw-r--r--src/lib/arch/IArchDaemon.h128
-rw-r--r--src/lib/arch/IArchFile.h105
-rw-r--r--src/lib/arch/IArchLog.h63
-rw-r--r--src/lib/arch/IArchMultithread.h273
-rw-r--r--src/lib/arch/IArchNetwork.h283
-rw-r--r--src/lib/arch/IArchSleep.h44
-rw-r--r--src/lib/arch/IArchString.cpp190
-rw-r--r--src/lib/arch/IArchString.h72
-rw-r--r--src/lib/arch/IArchSystem.h66
-rw-r--r--src/lib/arch/IArchTaskBar.h63
-rw-r--r--src/lib/arch/IArchTaskBarReceiver.h98
-rw-r--r--src/lib/arch/IArchTime.h41
-rw-r--r--src/lib/arch/XArch.h161
-rw-r--r--src/lib/arch/multibyte.h56
-rw-r--r--src/lib/arch/unix/ArchConsoleUnix.cpp23
-rw-r--r--src/lib/arch/unix/ArchConsoleUnix.h29
-rw-r--r--src/lib/arch/unix/ArchDaemonUnix.cpp132
-rw-r--r--src/lib/arch/unix/ArchDaemonUnix.h36
-rw-r--r--src/lib/arch/unix/ArchFileUnix.cpp163
-rw-r--r--src/lib/arch/unix/ArchFileUnix.h47
-rw-r--r--src/lib/arch/unix/ArchInternetUnix.cpp126
-rw-r--r--src/lib/arch/unix/ArchInternetUnix.h28
-rw-r--r--src/lib/arch/unix/ArchLogUnix.cpp84
-rw-r--r--src/lib/arch/unix/ArchLogUnix.h36
-rw-r--r--src/lib/arch/unix/ArchMultithreadPosix.cpp812
-rw-r--r--src/lib/arch/unix/ArchMultithreadPosix.h115
-rw-r--r--src/lib/arch/unix/ArchNetworkBSD.cpp1005
-rw-r--r--src/lib/arch/unix/ArchNetworkBSD.h105
-rw-r--r--src/lib/arch/unix/ArchSleepUnix.cpp94
-rw-r--r--src/lib/arch/unix/ArchSleepUnix.h33
-rw-r--r--src/lib/arch/unix/ArchStringUnix.cpp42
-rw-r--r--src/lib/arch/unix/ArchStringUnix.h34
-rw-r--r--src/lib/arch/unix/ArchSystemUnix.cpp80
-rw-r--r--src/lib/arch/unix/ArchSystemUnix.h38
-rw-r--r--src/lib/arch/unix/ArchTaskBarXWindows.cpp51
-rw-r--r--src/lib/arch/unix/ArchTaskBarXWindows.h35
-rw-r--r--src/lib/arch/unix/ArchTimeUnix.cpp52
-rw-r--r--src/lib/arch/unix/ArchTimeUnix.h33
-rw-r--r--src/lib/arch/unix/XArchUnix.cpp32
-rw-r--r--src/lib/arch/unix/XArchUnix.h33
-rw-r--r--src/lib/arch/vsnprintf.h67
-rw-r--r--src/lib/arch/win32/ArchConsoleWindows.cpp23
-rw-r--r--src/lib/arch/win32/ArchConsoleWindows.h29
-rw-r--r--src/lib/arch/win32/ArchDaemonWindows.cpp704
-rw-r--r--src/lib/arch/win32/ArchDaemonWindows.h151
-rw-r--r--src/lib/arch/win32/ArchFileWindows.cpp203
-rw-r--r--src/lib/arch/win32/ArchFileWindows.h47
-rw-r--r--src/lib/arch/win32/ArchInternetWindows.cpp224
-rw-r--r--src/lib/arch/win32/ArchInternetWindows.h28
-rw-r--r--src/lib/arch/win32/ArchLogWindows.cpp95
-rw-r--r--src/lib/arch/win32/ArchLogWindows.h42
-rw-r--r--src/lib/arch/win32/ArchMiscWindows.cpp524
-rw-r--r--src/lib/arch/win32/ArchMiscWindows.h202
-rw-r--r--src/lib/arch/win32/ArchMultithreadWindows.cpp705
-rw-r--r--src/lib/arch/win32/ArchMultithreadWindows.h116
-rw-r--r--src/lib/arch/win32/ArchNetworkWinsock.cpp985
-rw-r--r--src/lib/arch/win32/ArchNetworkWinsock.h111
-rw-r--r--src/lib/arch/win32/ArchSleepWindows.cpp61
-rw-r--r--src/lib/arch/win32/ArchSleepWindows.h33
-rw-r--r--src/lib/arch/win32/ArchStringWindows.cpp46
-rw-r--r--src/lib/arch/win32/ArchStringWindows.h34
-rw-r--r--src/lib/arch/win32/ArchSystemWindows.cpp166
-rw-r--r--src/lib/arch/win32/ArchSystemWindows.h39
-rw-r--r--src/lib/arch/win32/ArchTaskBarWindows.cpp514
-rw-r--r--src/lib/arch/win32/ArchTaskBarWindows.h114
-rw-r--r--src/lib/arch/win32/ArchTimeWindows.cpp89
-rw-r--r--src/lib/arch/win32/ArchTimeWindows.h33
-rw-r--r--src/lib/arch/win32/XArchWindows.cpp120
-rw-r--r--src/lib/arch/win32/XArchWindows.h49
-rw-r--r--src/lib/barrier/App.cpp329
-rw-r--r--src/lib/barrier/App.h200
-rw-r--r--src/lib/barrier/AppUtil.cpp52
-rw-r--r--src/lib/barrier/AppUtil.h40
-rw-r--r--src/lib/barrier/ArgParser.cpp519
-rw-r--r--src/lib/barrier/ArgParser.h63
-rw-r--r--src/lib/barrier/ArgsBase.cpp53
-rw-r--r--src/lib/barrier/ArgsBase.h54
-rw-r--r--src/lib/barrier/CMakeLists.txt40
-rw-r--r--src/lib/barrier/Chunk.cpp30
-rw-r--r--src/lib/barrier/Chunk.h30
-rw-r--r--src/lib/barrier/ClientApp.cpp560
-rw-r--r--src/lib/barrier/ClientApp.h83
-rw-r--r--src/lib/barrier/ClientArgs.cpp23
-rw-r--r--src/lib/barrier/ClientArgs.h30
-rw-r--r--src/lib/barrier/ClientTaskBarReceiver.cpp141
-rw-r--r--src/lib/barrier/ClientTaskBarReceiver.h95
-rw-r--r--src/lib/barrier/Clipboard.cpp118
-rw-r--r--src/lib/barrier/Clipboard.h71
-rw-r--r--src/lib/barrier/ClipboardChunk.cpp154
-rw-r--r--src/lib/barrier/ClipboardChunk.h60
-rw-r--r--src/lib/barrier/DragInformation.cpp158
-rw-r--r--src/lib/barrier/DragInformation.h53
-rw-r--r--src/lib/barrier/DropHelper.cpp53
-rw-r--r--src/lib/barrier/DropHelper.h27
-rw-r--r--src/lib/barrier/FileChunk.cpp156
-rw-r--r--src/lib/barrier/FileChunk.h46
-rw-r--r--src/lib/barrier/IApp.h47
-rw-r--r--src/lib/barrier/IAppUtil.h31
-rw-r--r--src/lib/barrier/IClient.h176
-rw-r--r--src/lib/barrier/IClipboard.cpp168
-rw-r--r--src/lib/barrier/IClipboard.h169
-rw-r--r--src/lib/barrier/IKeyState.cpp161
-rw-r--r--src/lib/barrier/IKeyState.h174
-rw-r--r--src/lib/barrier/INode.h25
-rw-r--r--src/lib/barrier/IPlatformScreen.cpp24
-rw-r--r--src/lib/barrier/IPlatformScreen.h227
-rw-r--r--src/lib/barrier/IPrimaryScreen.cpp91
-rw-r--r--src/lib/barrier/IPrimaryScreen.h165
-rw-r--r--src/lib/barrier/IScreen.h71
-rw-r--r--src/lib/barrier/IScreenSaver.h75
-rw-r--r--src/lib/barrier/ISecondaryScreen.h61
-rw-r--r--src/lib/barrier/KeyMap.cpp1344
-rw-r--r--src/lib/barrier/KeyMap.h512
-rw-r--r--src/lib/barrier/KeyState.cpp936
-rw-r--r--src/lib/barrier/KeyState.h232
-rw-r--r--src/lib/barrier/PacketStreamFilter.cpp198
-rw-r--r--src/lib/barrier/PacketStreamFilter.h59
-rw-r--r--src/lib/barrier/PlatformScreen.cpp123
-rw-r--r--src/lib/barrier/PlatformScreen.h127
-rw-r--r--src/lib/barrier/PortableTaskBarReceiver.cpp121
-rw-r--r--src/lib/barrier/PortableTaskBarReceiver.h96
-rw-r--r--src/lib/barrier/ProtocolUtil.cpp544
-rw-r--r--src/lib/barrier/ProtocolUtil.h96
-rw-r--r--src/lib/barrier/Screen.cpp559
-rw-r--r--src/lib/barrier/Screen.h345
-rw-r--r--src/lib/barrier/ServerApp.cpp859
-rw-r--r--src/lib/barrier/ServerApp.h127
-rw-r--r--src/lib/barrier/ServerArgs.cpp25
-rw-r--r--src/lib/barrier/ServerArgs.h32
-rw-r--r--src/lib/barrier/ServerTaskBarReceiver.cpp138
-rw-r--r--src/lib/barrier/ServerTaskBarReceiver.h98
-rw-r--r--src/lib/barrier/StreamChunker.cpp166
-rw-r--r--src/lib/barrier/StreamChunker.h45
-rw-r--r--src/lib/barrier/ToolApp.cpp205
-rw-r--r--src/lib/barrier/ToolApp.h37
-rw-r--r--src/lib/barrier/ToolArgs.cpp29
-rw-r--r--src/lib/barrier/ToolArgs.h34
-rw-r--r--src/lib/barrier/XBarrier.cpp133
-rw-r--r--src/lib/barrier/XBarrier.h135
-rw-r--r--src/lib/barrier/XScreen.cpp68
-rw-r--r--src/lib/barrier/XScreen.h68
-rw-r--r--src/lib/barrier/clipboard_types.h42
-rw-r--r--src/lib/barrier/key_types.cpp208
-rw-r--r--src/lib/barrier/key_types.h314
-rw-r--r--src/lib/barrier/mouse_types.h41
-rw-r--r--src/lib/barrier/option_types.h99
-rw-r--r--src/lib/barrier/protocol_types.cpp53
-rw-r--r--src/lib/barrier/protocol_types.h337
-rw-r--r--src/lib/barrier/unix/AppUtilUnix.cpp46
-rw-r--r--src/lib/barrier/unix/AppUtilUnix.h34
-rw-r--r--src/lib/barrier/win32/AppUtilWindows.cpp185
-rw-r--r--src/lib/barrier/win32/AppUtilWindows.h62
-rw-r--r--src/lib/barrier/win32/DaemonApp.cpp361
-rw-r--r--src/lib/barrier/win32/DaemonApp.h58
-rw-r--r--src/lib/base/CMakeLists.txt28
-rw-r--r--src/lib/base/ELevel.h38
-rw-r--r--src/lib/base/Event.cpp100
-rw-r--r--src/lib/base/Event.h126
-rw-r--r--src/lib/base/EventQueue.cpp658
-rw-r--r--src/lib/base/EventQueue.h200
-rw-r--r--src/lib/base/EventTypes.cpp203
-rw-r--r--src/lib/base/EventTypes.h754
-rw-r--r--src/lib/base/FunctionEventJob.cpp44
-rw-r--r--src/lib/base/FunctionEventJob.h39
-rw-r--r--src/lib/base/FunctionJob.cpp43
-rw-r--r--src/lib/base/FunctionJob.h39
-rw-r--r--src/lib/base/IEventJob.h33
-rw-r--r--src/lib/base/IEventQueue.h251
-rw-r--r--src/lib/base/IEventQueueBuffer.h101
-rw-r--r--src/lib/base/IJob.h31
-rw-r--r--src/lib/base/ILogOutputter.h69
-rw-r--r--src/lib/base/Log.cpp309
-rw-r--r--src/lib/base/Log.h211
-rw-r--r--src/lib/base/NonBlockingStream.cpp60
-rw-r--r--src/lib/base/NonBlockingStream.h49
-rw-r--r--src/lib/base/PriorityQueue.h138
-rw-r--r--src/lib/base/SimpleEventQueueBuffer.cpp101
-rw-r--r--src/lib/base/SimpleEventQueueBuffer.h52
-rw-r--r--src/lib/base/Stopwatch.cpp130
-rw-r--r--src/lib/base/Stopwatch.h109
-rw-r--r--src/lib/base/String.cpp295
-rw-r--r--src/lib/base/String.h135
-rw-r--r--src/lib/base/TMethodEventJob.h71
-rw-r--r--src/lib/base/TMethodJob.h68
-rw-r--r--src/lib/base/Unicode.cpp784
-rw-r--r--src/lib/base/Unicode.h144
-rw-r--r--src/lib/base/XBase.cpp76
-rw-r--r--src/lib/base/XBase.h125
-rw-r--r--src/lib/base/log_outputters.cpp337
-rw-r--r--src/lib/base/log_outputters.h172
-rw-r--r--src/lib/client/CMakeLists.txt28
-rw-r--r--src/lib/client/Client.cpp836
-rw-r--r--src/lib/client/Client.h227
-rw-r--r--src/lib/client/ServerProxy.cpp908
-rw-r--r--src/lib/client/ServerProxy.h133
-rw-r--r--src/lib/common/CMakeLists.txt24
-rw-r--r--src/lib/common/IInterface.h32
-rw-r--r--src/lib/common/MacOSXPrecomp.h23
-rw-r--r--src/lib/common/Version.cpp29
-rw-r--r--src/lib/common/Version.h39
-rw-r--r--src/lib/common/basic_types.h92
-rw-r--r--src/lib/common/common.h58
-rw-r--r--src/lib/common/stdbitset.h21
-rw-r--r--src/lib/common/stddeque.h21
-rw-r--r--src/lib/common/stdexcept.h23
-rw-r--r--src/lib/common/stdfstream.h22
-rw-r--r--src/lib/common/stdistream.h47
-rw-r--r--src/lib/common/stdlist.h21
-rw-r--r--src/lib/common/stdmap.h21
-rw-r--r--src/lib/common/stdostream.h25
-rw-r--r--src/lib/common/stdpost.h21
-rw-r--r--src/lib/common/stdpre.h31
-rw-r--r--src/lib/common/stdset.h21
-rw-r--r--src/lib/common/stdsstream.h376
-rw-r--r--src/lib/common/stdstring.h21
-rw-r--r--src/lib/common/stdvector.h21
-rw-r--r--src/lib/io/CMakeLists.txt24
-rw-r--r--src/lib/io/IStream.h120
-rw-r--r--src/lib/io/StreamBuffer.cpp146
-rw-r--r--src/lib/io/StreamBuffer.h79
-rw-r--r--src/lib/io/StreamFilter.cpp118
-rw-r--r--src/lib/io/StreamFilter.h73
-rw-r--r--src/lib/io/XIO.cpp51
-rw-r--r--src/lib/io/XIO.h49
-rw-r--r--src/lib/ipc/CMakeLists.txt28
-rw-r--r--src/lib/ipc/Ipc.cpp24
-rw-r--r--src/lib/ipc/Ipc.h52
-rw-r--r--src/lib/ipc/IpcClient.cpp108
-rw-r--r--src/lib/ipc/IpcClient.h64
-rw-r--r--src/lib/ipc/IpcClientProxy.cpp194
-rw-r--r--src/lib/ipc/IpcClientProxy.h55
-rw-r--r--src/lib/ipc/IpcLogOutputter.cpp228
-rw-r--r--src/lib/ipc/IpcLogOutputter.h119
-rw-r--r--src/lib/ipc/IpcMessage.cpp69
-rw-r--r--src/lib/ipc/IpcMessage.h85
-rw-r--r--src/lib/ipc/IpcServer.cpp187
-rw-r--r--src/lib/ipc/IpcServer.h92
-rw-r--r--src/lib/ipc/IpcServerProxy.cpp123
-rw-r--r--src/lib/ipc/IpcServerProxy.h46
-rw-r--r--src/lib/mt/CMakeLists.txt24
-rw-r--r--src/lib/mt/CondVar.cpp91
-rw-r--r--src/lib/mt/CondVar.h225
-rw-r--r--src/lib/mt/Lock.cpp42
-rw-r--r--src/lib/mt/Lock.h49
-rw-r--r--src/lib/mt/Mutex.cpp58
-rw-r--r--src/lib/mt/Mutex.h79
-rw-r--r--src/lib/mt/Thread.cpp187
-rw-r--r--src/lib/mt/Thread.h210
-rw-r--r--src/lib/mt/XMT.cpp29
-rw-r--r--src/lib/mt/XMT.h30
-rw-r--r--src/lib/mt/XThread.h37
-rw-r--r--src/lib/net/CMakeLists.txt28
-rw-r--r--src/lib/net/IDataSocket.cpp39
-rw-r--r--src/lib/net/IDataSocket.h73
-rw-r--r--src/lib/net/IListenSocket.h51
-rw-r--r--src/lib/net/ISocket.h60
-rw-r--r--src/lib/net/ISocketFactory.h48
-rw-r--r--src/lib/net/ISocketMultiplexerJob.h76
-rw-r--r--src/lib/net/NetworkAddress.cpp214
-rw-r--r--src/lib/net/NetworkAddress.h123
-rw-r--r--src/lib/net/SecureListenSocket.cpp85
-rw-r--r--src/lib/net/SecureListenSocket.h36
-rw-r--r--src/lib/net/SecureSocket.cpp867
-rw-r--r--src/lib/net/SecureSocket.h95
-rw-r--r--src/lib/net/SocketMultiplexer.cpp352
-rw-r--r--src/lib/net/SocketMultiplexer.h111
-rw-r--r--src/lib/net/TCPListenSocket.cpp158
-rw-r--r--src/lib/net/TCPListenSocket.h60
-rw-r--r--src/lib/net/TCPSocket.cpp596
-rw-r--r--src/lib/net/TCPSocket.h116
-rw-r--r--src/lib/net/TCPSocketFactory.cpp68
-rw-r--r--src/lib/net/TCPSocketFactory.h44
-rw-r--r--src/lib/net/TSocketMultiplexerMethodJob.h109
-rw-r--r--src/lib/net/XSocket.cpp117
-rw-r--r--src/lib/net/XSocket.h98
-rw-r--r--src/lib/platform/CMakeLists.txt49
-rw-r--r--src/lib/platform/IMSWindowsClipboardFacade.h36
-rw-r--r--src/lib/platform/IOSXKeyResource.cpp189
-rw-r--r--src/lib/platform/IOSXKeyResource.h36
-rw-r--r--src/lib/platform/ImmuneKeysReader.cpp53
-rw-r--r--src/lib/platform/ImmuneKeysReader.h34
-rw-r--r--src/lib/platform/MSWindowsClipboard.cpp232
-rw-r--r--src/lib/platform/MSWindowsClipboard.h113
-rw-r--r--src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp149
-rw-r--r--src/lib/platform/MSWindowsClipboardAnyTextConverter.h57
-rw-r--r--src/lib/platform/MSWindowsClipboardBitmapConverter.cpp152
-rw-r--r--src/lib/platform/MSWindowsClipboardBitmapConverter.h36
-rw-r--r--src/lib/platform/MSWindowsClipboardFacade.cpp31
-rw-r--r--src/lib/platform/MSWindowsClipboardFacade.h29
-rw-r--r--src/lib/platform/MSWindowsClipboardHTMLConverter.cpp120
-rw-r--r--src/lib/platform/MSWindowsClipboardHTMLConverter.h45
-rw-r--r--src/lib/platform/MSWindowsClipboardTextConverter.cpp60
-rw-r--r--src/lib/platform/MSWindowsClipboardTextConverter.h37
-rw-r--r--src/lib/platform/MSWindowsClipboardUTF16Converter.cpp60
-rw-r--r--src/lib/platform/MSWindowsClipboardUTF16Converter.h37
-rw-r--r--src/lib/platform/MSWindowsDebugOutputter.cpp58
-rw-r--r--src/lib/platform/MSWindowsDebugOutputter.h39
-rw-r--r--src/lib/platform/MSWindowsDesks.cpp923
-rw-r--r--src/lib/platform/MSWindowsDesks.h297
-rw-r--r--src/lib/platform/MSWindowsDropTarget.cpp178
-rw-r--r--src/lib/platform/MSWindowsDropTarget.h59
-rw-r--r--src/lib/platform/MSWindowsEventQueueBuffer.cpp146
-rw-r--r--src/lib/platform/MSWindowsEventQueueBuffer.h50
-rw-r--r--src/lib/platform/MSWindowsHook.cpp629
-rw-r--r--src/lib/platform/MSWindowsHook.h39
-rw-r--r--src/lib/platform/MSWindowsHookResource.cpp33
-rw-r--r--src/lib/platform/MSWindowsHookResource.h20
-rw-r--r--src/lib/platform/MSWindowsKeyState.cpp1406
-rw-r--r--src/lib/platform/MSWindowsKeyState.h233
-rw-r--r--src/lib/platform/MSWindowsScreen.cpp1959
-rw-r--r--src/lib/platform/MSWindowsScreen.h346
-rw-r--r--src/lib/platform/MSWindowsScreenSaver.cpp359
-rw-r--r--src/lib/platform/MSWindowsScreenSaver.h89
-rw-r--r--src/lib/platform/MSWindowsSession.cpp195
-rw-r--r--src/lib/platform/MSWindowsSession.h52
-rw-r--r--src/lib/platform/MSWindowsUtil.cpp64
-rw-r--r--src/lib/platform/MSWindowsUtil.h40
-rw-r--r--src/lib/platform/MSWindowsWatchdog.cpp611
-rw-r--r--src/lib/platform/MSWindowsWatchdog.h99
-rw-r--r--src/lib/platform/OSXClipboard.cpp259
-rw-r--r--src/lib/platform/OSXClipboard.h95
-rw-r--r--src/lib/platform/OSXClipboardAnyBitmapConverter.cpp48
-rw-r--r--src/lib/platform/OSXClipboardAnyBitmapConverter.h48
-rw-r--r--src/lib/platform/OSXClipboardAnyTextConverter.cpp90
-rw-r--r--src/lib/platform/OSXClipboardAnyTextConverter.h53
-rw-r--r--src/lib/platform/OSXClipboardBMPConverter.cpp134
-rw-r--r--src/lib/platform/OSXClipboardBMPConverter.h44
-rw-r--r--src/lib/platform/OSXClipboardHTMLConverter.cpp95
-rw-r--r--src/lib/platform/OSXClipboardHTMLConverter.h44
-rw-r--r--src/lib/platform/OSXClipboardTextConverter.cpp93
-rw-r--r--src/lib/platform/OSXClipboardTextConverter.h42
-rw-r--r--src/lib/platform/OSXClipboardUTF16Converter.cpp55
-rw-r--r--src/lib/platform/OSXClipboardUTF16Converter.h37
-rw-r--r--src/lib/platform/OSXDragSimulator.h34
-rw-r--r--src/lib/platform/OSXDragSimulator.m102
-rw-r--r--src/lib/platform/OSXDragView.h34
-rw-r--r--src/lib/platform/OSXDragView.m177
-rw-r--r--src/lib/platform/OSXEventQueueBuffer.cpp143
-rw-r--r--src/lib/platform/OSXEventQueueBuffer.h47
-rw-r--r--src/lib/platform/OSXKeyState.cpp912
-rw-r--r--src/lib/platform/OSXKeyState.h180
-rw-r--r--src/lib/platform/OSXMediaKeySimulator.h30
-rw-r--r--src/lib/platform/OSXMediaKeySimulator.m92
-rw-r--r--src/lib/platform/OSXMediaKeySupport.h33
-rw-r--r--src/lib/platform/OSXMediaKeySupport.m154
-rw-r--r--src/lib/platform/OSXPasteboardPeeker.h32
-rw-r--r--src/lib/platform/OSXPasteboardPeeker.m37
-rw-r--r--src/lib/platform/OSXScreen.h349
-rw-r--r--src/lib/platform/OSXScreen.mm2162
-rw-r--r--src/lib/platform/OSXScreenSaver.cpp201
-rw-r--r--src/lib/platform/OSXScreenSaver.h59
-rw-r--r--src/lib/platform/OSXScreenSaverControl.h54
-rw-r--r--src/lib/platform/OSXScreenSaverUtil.h40
-rw-r--r--src/lib/platform/OSXScreenSaverUtil.m83
-rw-r--r--src/lib/platform/OSXUchrKeyResource.cpp296
-rw-r--r--src/lib/platform/OSXUchrKeyResource.h55
-rw-r--r--src/lib/platform/XWindowsClipboard.cpp1525
-rw-r--r--src/lib/platform/XWindowsClipboard.h378
-rw-r--r--src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp191
-rw-r--r--src/lib/platform/XWindowsClipboardAnyBitmapConverter.h60
-rw-r--r--src/lib/platform/XWindowsClipboardBMPConverter.cpp143
-rw-r--r--src/lib/platform/XWindowsClipboardBMPConverter.h40
-rw-r--r--src/lib/platform/XWindowsClipboardHTMLConverter.cpp67
-rw-r--r--src/lib/platform/XWindowsClipboardHTMLConverter.h42
-rw-r--r--src/lib/platform/XWindowsClipboardTextConverter.cpp79
-rw-r--r--src/lib/platform/XWindowsClipboardTextConverter.h42
-rw-r--r--src/lib/platform/XWindowsClipboardUCS2Converter.cpp67
-rw-r--r--src/lib/platform/XWindowsClipboardUCS2Converter.h42
-rw-r--r--src/lib/platform/XWindowsClipboardUTF8Converter.cpp65
-rw-r--r--src/lib/platform/XWindowsClipboardUTF8Converter.h42
-rw-r--r--src/lib/platform/XWindowsEventQueueBuffer.cpp291
-rw-r--r--src/lib/platform/XWindowsEventQueueBuffer.h64
-rw-r--r--src/lib/platform/XWindowsKeyState.cpp867
-rw-r--r--src/lib/platform/XWindowsKeyState.h174
-rw-r--r--src/lib/platform/XWindowsScreen.cpp2096
-rw-r--r--src/lib/platform/XWindowsScreen.h252
-rw-r--r--src/lib/platform/XWindowsScreenSaver.cpp605
-rw-r--r--src/lib/platform/XWindowsScreenSaver.h169
-rw-r--r--src/lib/platform/XWindowsUtil.cpp1790
-rw-r--r--src/lib/platform/XWindowsUtil.h187
-rw-r--r--src/lib/platform/synwinhk.h66
-rw-r--r--src/lib/server/BaseClientProxy.cpp56
-rw-r--r--src/lib/server/BaseClientProxy.h99
-rw-r--r--src/lib/server/CMakeLists.txt30
-rw-r--r--src/lib/server/ClientListener.cpp256
-rw-r--r--src/lib/server/ClientListener.h91
-rw-r--r--src/lib/server/ClientProxy.cpp61
-rw-r--r--src/lib/server/ClientProxy.h91
-rw-r--r--src/lib/server/ClientProxy1_0.cpp484
-rw-r--r--src/lib/server/ClientProxy1_0.h107
-rw-r--r--src/lib/server/ClientProxy1_1.cpp61
-rw-r--r--src/lib/server/ClientProxy1_1.h34
-rw-r--r--src/lib/server/ClientProxy1_2.cpp44
-rw-r--r--src/lib/server/ClientProxy1_2.h33
-rw-r--r--src/lib/server/ClientProxy1_3.cpp129
-rw-r--r--src/lib/server/ClientProxy1_3.h48
-rw-r--r--src/lib/server/ClientProxy1_4.cpp66
-rw-r--r--src/lib/server/ClientProxy1_4.h46
-rw-r--r--src/lib/server/ClientProxy1_5.cpp110
-rw-r--r--src/lib/server/ClientProxy1_5.h41
-rw-r--r--src/lib/server/ClientProxy1_6.cpp100
-rw-r--r--src/lib/server/ClientProxy1_6.h39
-rw-r--r--src/lib/server/ClientProxyUnknown.cpp295
-rw-r--r--src/lib/server/ClientProxyUnknown.h71
-rw-r--r--src/lib/server/Config.cpp2335
-rw-r--r--src/lib/server/Config.h549
-rw-r--r--src/lib/server/InputFilter.cpp1090
-rw-r--r--src/lib/server/InputFilter.h361
-rw-r--r--src/lib/server/PrimaryClient.cpp274
-rw-r--r--src/lib/server/PrimaryClient.h156
-rw-r--r--src/lib/server/Server.cpp2405
-rw-r--r--src/lib/server/Server.h483
-rw-r--r--src/test/CMakeLists.txt33
-rw-r--r--src/test/global/TestEventQueue.cpp53
-rw-r--r--src/test/global/TestEventQueue.h38
-rw-r--r--src/test/global/gmock.h32
-rw-r--r--src/test/global/gtest.h29
-rw-r--r--src/test/guitests/guitests.pro16
-rw-r--r--src/test/guitests/src/VersionCheckerTests.cpp47
-rw-r--r--src/test/guitests/src/VersionCheckerTests.h28
-rw-r--r--src/test/guitests/src/main.cpp26
-rw-r--r--src/test/integtests/CMakeLists.txt71
-rw-r--r--src/test/integtests/Main.cpp108
-rw-r--r--src/test/integtests/arch/ArchInternetTests.cpp37
-rw-r--r--src/test/integtests/ipc/IpcTests.cpp211
-rw-r--r--src/test/integtests/net/NetworkTests.cpp525
-rw-r--r--src/test/integtests/platform/MSWindowsClipboardTests.cpp229
-rw-r--r--src/test/integtests/platform/MSWindowsKeyStateTests.cpp144
-rw-r--r--src/test/integtests/platform/OSXClipboardTests.cpp164
-rw-r--r--src/test/integtests/platform/OSXKeyStateTests.cpp119
-rw-r--r--src/test/integtests/platform/OSXScreenTests.cpp51
-rw-r--r--src/test/integtests/platform/XWindowsClipboardTests.cpp157
-rw-r--r--src/test/integtests/platform/XWindowsKeyStateTests.cpp246
-rw-r--r--src/test/integtests/platform/XWindowsScreenSaverTests.cpp48
-rw-r--r--src/test/integtests/platform/XWindowsScreenTests.cpp41
-rw-r--r--src/test/mock/barrier/MockApp.h44
-rw-r--r--src/test/mock/barrier/MockArgParser.h33
-rw-r--r--src/test/mock/barrier/MockEventQueue.h67
-rw-r--r--src/test/mock/barrier/MockKeyMap.h36
-rw-r--r--src/test/mock/barrier/MockKeyState.h57
-rw-r--r--src/test/mock/barrier/MockScreen.h36
-rw-r--r--src/test/mock/io/MockStream.h44
-rw-r--r--src/test/mock/ipc/MockIpcServer.h68
-rw-r--r--src/test/mock/server/MockConfig.h32
-rw-r--r--src/test/mock/server/MockInputFilter.h30
-rw-r--r--src/test/mock/server/MockPrimaryClient.h41
-rw-r--r--src/test/mock/server/MockServer.h32
-rw-r--r--src/test/unittests/CMakeLists.txt71
-rw-r--r--src/test/unittests/Main.cpp50
-rw-r--r--src/test/unittests/barrier/ArgParserTests.cpp207
-rw-r--r--src/test/unittests/barrier/ClientArgsParsingTests.cpp95
-rw-r--r--src/test/unittests/barrier/ClipboardChunkTests.cpp128
-rw-r--r--src/test/unittests/barrier/ClipboardTests.cpp404
-rw-r--r--src/test/unittests/barrier/DeprecatedArgsParsingTests.cpp50
-rw-r--r--src/test/unittests/barrier/GenericArgsParsingTests.cpp315
-rw-r--r--src/test/unittests/barrier/KeyMapTests.cpp216
-rw-r--r--src/test/unittests/barrier/KeyStateTests.cpp488
-rw-r--r--src/test/unittests/barrier/ServerArgsParsingTests.cpp66
-rw-r--r--src/test/unittests/base/StringTests.cpp177
-rw-r--r--src/test/unittests/ipc/IpcLogOutputterTests.cpp165
-rw-r--r--src/test/unittests/platform/OSXKeyStateTests.cpp57
787 files changed, 170142 insertions, 0 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..1411b05
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,30 @@
+### Operating Systems ###
+
+Server: microOS Tiara
+
+Client: Applesoft Windy OS 10
+
+**READ ME, DELETE ME**: On Windows, hold the Windows key and press 'r', type 'winver' and hit return to get your OS version. On Mac, hit the Apple menu (top left of the screen) and check 'About this Mac'. Linux users... you know what you're using ;)
+
+### Barrier Version ###
+
+1.9.Ï€
+
+**READ ME, DELETE ME**: Go to the 'Help' (on Windows) or 'Barrier' (on macOS) menu and then 'About Barrier' to check your version. Verify that you are using the same version across all of your machines, and that your issue still occurs with the latest release available at https://github.com/debauchee/barrier/
+
+### Steps to reproduce bug ###
+
+**READ ME, DELETE ME**: Try to be succinct. If your bug is intermittent, try and describe what you're doing when it happens most.
+
+1. Click things.
+2. Type things.
+3. Bug occurs.
+4. ...
+
+### Other info ###
+
+* When did the problem start to occur? When I...
+* Is there a way to work around it? No/Yes, you can...
+* Does this bug prevent you from using Barrier entirely? Yes/No
+
+Put anything else you can think of here.
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eb253b1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,22 @@
+build_env.*
+config.h
+.DS_Store
+*.pyc
+*.o
+*~
+\.*.swp
+*build-gui-Desktop_Qt*
+/bin
+/lib
+/build
+/CMakeFiles
+/ext/cryptopp562
+/ext/openssl
+/src/gui/Makefile*
+/src/gui/object_script*
+/src/gui/tmp
+/src/gui/ui_*
+src/gui/gui.pro.user*
+src/gui/.qmake.stash
+src/gui/.rnd
+src/setup/win32/barrier.suo
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..82f36c5
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: cpp
+before_install:
+- sudo apt-get update -qq
+- sudo apt-get install -qq libxtst-dev
+- sudo apt-get install -qq qtdeclarative5-dev
+- sudo apt-get install -qq libavahi-compat-libdnssd-dev
+script: sh -x ./clean_build.sh
+# skip install phase since we have a customized install package
+# creation tool for each supported platform
+install: true
diff --git a/Build.properties b/Build.properties
new file mode 100644
index 0000000..b8b83fd
--- /dev/null
+++ b/Build.properties
@@ -0,0 +1,7 @@
+#
+# Barrier build parameters
+#
+BARRIER_VERSION_MAJOR = 1
+BARRIER_VERSION_MINOR = 9
+BARRIER_VERSION_PATCH = 0
+BARRIER_VERSION_STAGE = snapshot
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..5c4ba6d
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,395 @@
+# Barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2018 Debauchee Open Source Group
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+cmake_minimum_required (VERSION 3.4)
+project (barrier C CXX)
+
+option (BARRIER_BUILD_GUI "Build the GUI" ON)
+option (BARRIER_BUILD_INSTALLER "Build the installer" ON)
+
+set (CMAKE_CXX_STANDARD 14)
+set (CMAKE_CXX_EXTENSIONS OFF)
+set (CMAKE_CXX_STANDARD_REQUIRED ON)
+set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
+set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
+
+if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
+ add_definitions (-DNDEBUG)
+endif()
+
+include (cmake/Version.cmake)
+include (cmake/Package.cmake)
+
+# TODO: Find out why we need these, and remove them
+if (COMMAND cmake_policy)
+ cmake_policy (SET CMP0003 NEW)
+ cmake_policy (SET CMP0005 NEW)
+endif()
+
+# Add headers to source list
+if (${CMAKE_GENERATOR} STREQUAL "Unix Makefiles")
+ set (BARRIER_ADD_HEADERS FALSE)
+else()
+ set (BARRIER_ADD_HEADERS TRUE)
+endif()
+
+set (libs)
+include_directories (BEFORE SYSTEM ./ext/gtest/include)
+
+if (UNIX)
+ if (NOT APPLE)
+ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
+ endif()
+
+ # For config.h, detect the libraries, functions, etc.
+ include (CheckIncludeFiles)
+ include (CheckLibraryExists)
+ include (CheckFunctionExists)
+ include (CheckTypeSize)
+ include (CheckIncludeFileCXX)
+ include (CheckSymbolExists)
+ include (CheckCSourceCompiles)
+
+ check_include_file_cxx (istream HAVE_ISTREAM)
+ check_include_file_cxx (ostream HAVE_OSTREAM)
+ check_include_file_cxx (sstream HAVE_SSTREAM)
+
+ check_include_files (inttypes.h HAVE_INTTYPES_H)
+ check_include_files (locale.h HAVE_LOCALE_H)
+ check_include_files (memory.h HAVE_MEMORY_H)
+ check_include_files (stdlib.h HAVE_STDLIB_H)
+ check_include_files (strings.h HAVE_STRINGS_H)
+ check_include_files (string.h HAVE_STRING_H)
+ check_include_files (sys/select.h HAVE_SYS_SELECT_H)
+ check_include_files (sys/socket.h HAVE_SYS_SOCKET_H)
+ check_include_files (sys/stat.h HAVE_SYS_STAT_H)
+ check_include_files (sys/time.h HAVE_SYS_TIME_H)
+ check_include_files (sys/utsname.h HAVE_SYS_UTSNAME_H)
+ check_include_files (unistd.h HAVE_UNISTD_H)
+ check_include_files (wchar.h HAVE_WCHAR_H)
+
+ check_function_exists (getpwuid_r HAVE_GETPWUID_R)
+ check_function_exists (gmtime_r HAVE_GMTIME_R)
+ check_function_exists (nanosleep HAVE_NANOSLEEP)
+ check_function_exists (poll HAVE_POLL)
+ check_function_exists (sigwait HAVE_POSIX_SIGWAIT)
+ check_function_exists (strftime HAVE_STRFTIME)
+ check_function_exists (vsnprintf HAVE_VSNPRINTF)
+ check_function_exists (inet_aton HAVE_INET_ATON)
+
+ # For some reason, the check_function_exists macro doesn't detect
+ # the inet_aton on some pure Unix platforms (e.g. sunos5). So we
+ # need to do a more detailed check and also include some extra libs.
+ if (NOT HAVE_INET_ATON)
+ set (CMAKE_REQUIRED_LIBRARIES nsl)
+
+ check_c_source_compiles (
+ "#include <arpa/inet.h>\n int main() { inet_aton (0, 0); }"
+ HAVE_INET_ATON_ADV)
+
+ set (CMAKE_REQUIRED_LIBRARIES)
+
+ if (HAVE_INET_ATON_ADV)
+ # Override the previous fail.
+ set (HAVE_INET_ATON 1)
+
+ # Assume that both nsl and socket will be needed,
+ # it seems safe to add socket on the back of nsl,
+ # since socket only ever needed when nsl is needed.
+ list (APPEND libs nsl socket)
+ endif()
+
+ endif()
+
+ check_type_size (char SIZEOF_CHAR)
+ check_type_size (int SIZEOF_INT)
+ check_type_size (long SIZEOF_LONG)
+ check_type_size (short SIZEOF_SHORT)
+
+ # pthread is used on both Linux and Mac
+ check_library_exists ("pthread" pthread_create "" HAVE_PTHREAD)
+ if (HAVE_PTHREAD)
+ list (APPEND libs pthread)
+ else()
+ message (FATAL_ERROR "Missing library: pthread")
+ endif()
+
+ # curl is used on both Linux and Mac
+ find_package (CURL)
+ if (CURL_FOUND)
+ list (APPEND libs curl)
+ else()
+ message (FATAL_ERROR "Missing library: curl")
+ endif()
+
+ if (APPLE)
+ set (CMAKE_CXX_FLAGS "--sysroot ${CMAKE_OSX_SYSROOT} ${CMAKE_CXX_FLAGS} -DGTEST_USE_OWN_TR1_TUPLE=1")
+
+ find_library (lib_ScreenSaver ScreenSaver)
+ find_library (lib_IOKit IOKit)
+ find_library (lib_ApplicationServices ApplicationServices)
+ find_library (lib_Foundation Foundation)
+ find_library (lib_Carbon Carbon)
+
+ list (APPEND libs
+ ${lib_ScreenSaver}
+ ${lib_IOKit}
+ ${lib_ApplicationServices}
+ ${lib_Foundation}
+ ${lib_Carbon}
+ )
+
+ else() # not-apple
+ # FreeBSD uses /usr/local for anything not part of base
+ # Also package avahi-libdns puts dns_sd.h a bit deeper
+ if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
+ set (CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};/usr/local/include;/usr/local/include/avahi-compat-libdns_sd")
+ set (CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -L/usr/local/lib")
+ include_directories("/usr/local/include" "/usr/local/include/avahi-compat-libdns_sd")
+ link_directories("/usr/local/lib")
+ endif()
+
+ set (XKBlib "X11/Xlib.h;X11/XKBlib.h")
+ set (CMAKE_EXTRA_INCLUDE_FILES "${XKBlib};X11/extensions/Xrandr.h")
+ check_type_size ("XRRNotifyEvent" X11_EXTENSIONS_XRANDR_H)
+ set (HAVE_X11_EXTENSIONS_XRANDR_H "${X11_EXTENSIONS_XRANDR_H}")
+ set (CMAKE_EXTRA_INCLUDE_FILES)
+
+ check_include_files ("${XKBlib};X11/extensions/dpms.h" HAVE_X11_EXTENSIONS_DPMS_H)
+ check_include_files ("X11/extensions/Xinerama.h" HAVE_X11_EXTENSIONS_XINERAMA_H)
+ check_include_files ("${XKBlib};X11/extensions/XKBstr.h" HAVE_X11_EXTENSIONS_XKBSTR_H)
+ check_include_files ("X11/extensions/XKB.h" HAVE_XKB_EXTENSION)
+ check_include_files ("X11/extensions/XTest.h" HAVE_X11_EXTENSIONS_XTEST_H)
+ check_include_files ("${XKBlib}" HAVE_X11_XKBLIB_H)
+ check_include_files ("X11/extensions/XInput2.h" HAVE_XI2)
+ check_include_files ("dns_sd.h" HAVE_DNSSD)
+
+ if (HAVE_X11_EXTENSIONS_DPMS_H)
+ # Assume that function prototypes declared, when include exists.
+ set (HAVE_DPMS_PROTOTYPES 1)
+ endif()
+
+ if (NOT HAVE_X11_XKBLIB_H)
+ message (FATAL_ERROR "Missing header: " ${XKBlib})
+ endif()
+
+ if (NOT HAVE_DNSSD)
+ message (FATAL_ERROR "Missing header: dns_sd.h")
+ endif()
+
+ check_library_exists ("SM;ICE" IceConnectionNumber "" HAVE_ICE)
+ check_library_exists ("Xext;X11" DPMSQueryExtension "" HAVE_Xext)
+ check_library_exists ("Xtst;Xext;X11" XTestQueryExtension "" HAVE_Xtst)
+ check_library_exists ("Xinerama" XineramaQueryExtension "" HAVE_Xinerama)
+ check_library_exists ("Xi" XISelectEvents "" HAVE_Xi)
+ check_library_exists ("Xrandr" XRRQueryExtension "" HAVE_Xrandr)
+
+ if (HAVE_ICE)
+
+ # Assume we have SM if we have ICE.
+ set (HAVE_SM 1)
+ list (APPEND libs SM ICE)
+
+ endif()
+
+ if (HAVE_Xtst)
+
+ # Xtxt depends on X11.
+ set (HAVE_X11 1)
+ list (APPEND libs Xtst X11)
+
+ else()
+
+ message (FATAL_ERROR "Missing library: Xtst")
+
+ endif()
+
+ if (HAVE_Xext)
+ list (APPEND libs Xext)
+ endif()
+
+ if (HAVE_Xinerama)
+ list (APPEND libs Xinerama)
+ else (HAVE_Xinerama)
+ if (HAVE_X11_EXTENSIONS_XINERAMA_H)
+ set (HAVE_X11_EXTENSIONS_XINERAMA_H 0)
+ message (WARNING "Old Xinerama implementation detected, disabled")
+ endif()
+ endif()
+
+ if (HAVE_Xrandr)
+ list (APPEND libs Xrandr)
+ endif()
+
+ # this was outside of the linux scope,
+ # not sure why, moving it back inside.
+ if (HAVE_Xi)
+ list (APPEND libs Xi)
+ endif()
+
+ endif()
+
+ # For config.h, set some static values; it may be a good idea to make
+ # these values dynamic for non-standard UNIX compilers.
+ set (ACCEPT_TYPE_ARG3 socklen_t)
+ set (HAVE_CXX_BOOL 1)
+ set (HAVE_CXX_CASTS 1)
+ set (HAVE_CXX_EXCEPTIONS 1)
+ set (HAVE_CXX_MUTABLE 1)
+ set (HAVE_CXX_STDLIB 1)
+ set (HAVE_PTHREAD_SIGNAL 1)
+ set (SELECT_TYPE_ARG1 int)
+ set (SELECT_TYPE_ARG234 " (fd_set *)")
+ set (SELECT_TYPE_ARG5 " (struct timeval *)")
+ set (STDC_HEADERS 1)
+ set (TIME_WITH_SYS_TIME 1)
+ set (HAVE_SOCKLEN_T 1)
+
+ # For config.h, save the results based on a template (config.h.in).
+ configure_file (res/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/lib/config.h)
+
+ add_definitions (-DSYSAPI_UNIX=1 -DHAVE_CONFIG_H)
+
+ if (APPLE)
+ add_definitions (-DWINAPI_CARBON=1 -D_THREAD_SAFE)
+ else()
+ add_definitions (-DWINAPI_XWINDOWS=1)
+ endif()
+
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /D _BIND_TO_CURRENT_VCLIBS_VERSION=1")
+ set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD /O2 /Ob2")
+
+ list (APPEND libs Wtsapi32 Userenv Wininet comsuppw Shlwapi)
+
+ add_definitions (
+ /DWIN32
+ /D_WINDOWS
+ /D_CRT_SECURE_NO_WARNINGS
+ /DBARRIER_VERSION=\"${BARRIER_VERSION}\"
+ /D_XKEYCHECK_H
+ )
+endif()
+
+#
+# OpenSSL
+#
+if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+ set (OPENSSL_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/ext/openssl/windows)
+ if (CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set (OPENSSL_ROOT "${OPENSSL_ROOT}/x64")
+ else()
+ set (OPENSSL_ROOT "${OPENSSL_ROOT}/x86")
+ endif()
+
+ include_directories (BEFORE SYSTEM ${OPENSSL_ROOT}/include)
+ set (OPENSSL_LIBS
+ ${OPENSSL_ROOT}/lib/libeay32.lib
+ ${OPENSSL_ROOT}/lib/ssleay32.lib
+ )
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ set (OPENSSL_ROOT /usr/local/opt/openssl)
+
+ include_directories (BEFORE SYSTEM ${OPENSSL_ROOT}/include)
+ set (OPENSSL_LIBS
+ ${OPENSSL_ROOT}/lib/libssl.a
+ ${OPENSSL_ROOT}/lib/libcrypto.a
+ )
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ set (OPENSSL_LIBS ssl crypto)
+else()
+ find_library (lib_ssl ssl)
+ find_library (lib_crypto crypto)
+ if (NOT lib_ssl)
+ message(FATAL_ERROR "openssl library not found")
+ elseif (NOT lib_crypto)
+ message(FATAL_ERROR "crypto library not found")
+ endif()
+ set (OPENSSL_LIBS ${lib_ssl} ${lib_crypto})
+endif()
+
+#
+# Configure_file... but for directories, recursively.
+#
+macro (configure_files srcDir destDir)
+ message (STATUS "Configuring directory ${destDir}")
+ make_directory (${destDir})
+
+ file (GLOB_RECURSE sourceFiles RELATIVE ${srcDir} ${srcDir}/*)
+ file (GLOB_RECURSE templateFiles LIST_DIRECTORIES false RELATIVE ${srcDir} ${srcDir}/*.in)
+ list (REMOVE_ITEM sourceFiles ${templateFiles})
+
+ foreach (sourceFile ${sourceFiles})
+ set (sourceFilePath ${srcDir}/${sourceFile})
+ if (IS_DIRECTORY ${sourceFilePath})
+ message (STATUS "Copying directory ${sourceFile}")
+ make_directory (${destDir/${sourceFile})
+ else()
+ message (STATUS "Copying file ${sourceFile}")
+ configure_file (${sourceFilePath} ${destDir}/${sourceFile} COPYONLY)
+ endif()
+ endforeach (sourceFile)
+
+ foreach (templateFile ${templateFiles})
+ set (sourceTemplateFilePath ${srcDir}/${templateFile})
+ string (REGEX REPLACE "\.in$" "" templateFile ${templateFile})
+ message (STATUS "Configuring file ${templateFile}")
+ configure_file (${sourceTemplateFilePath} ${destDir}/${templateFile} @ONLY)
+ endforeach (templateFile)
+endmacro (configure_files)
+
+if (${BARRIER_BUILD_INSTALLER})
+#
+# macOS app Bundle
+#
+if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ set (CMAKE_INSTALL_RPATH "@loader_path/../Libraries;@loader_path/../Frameworks")
+ set (BARRIER_BUNDLE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dist/macos/bundle)
+ set (BARRIER_BUNDLE_DIR ${CMAKE_BINARY_DIR}/bundle)
+ set (BARRIER_BUNDLE_APP_DIR ${BARRIER_BUNDLE_DIR}/Barrier.app)
+ set (BARRIER_BUNDLE_BINARY_DIR ${BARRIER_BUNDLE_APP_DIR}/Contents/MacOS)
+
+ configure_files (${BARRIER_BUNDLE_SOURCE_DIR} ${BARRIER_BUNDLE_DIR})
+endif()
+
+#
+# Windows installer
+#
+if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
+ message (STATUS "Configuring the wix installer")
+ configure_files (${CMAKE_CURRENT_SOURCE_DIR}/dist/wix ${CMAKE_BINARY_DIR}/installer-wix)
+ message (STATUS "Configuring the inno installer")
+ configure_files (${CMAKE_CURRENT_SOURCE_DIR}/dist/inno ${CMAKE_BINARY_DIR}/installer-inno)
+endif()
+
+#
+# Linux installation
+#
+if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ configure_files (${CMAKE_CURRENT_SOURCE_DIR}/dist/rpm ${CMAKE_BINARY_DIR}/rpm)
+ install(FILES res/barrier.svg DESTINATION share/icons/hicolor/scalable/apps)
+ if("${VERSION_MAJOR}" STREQUAL "2")
+ install(FILES res/barrier2.desktop DESTINATION share/applications)
+ else()
+ install(FILES res/barrier.desktop DESTINATION share/applications)
+ endif()
+endif()
+
+else()
+ message (STATUS "NOT configuring the installer")
+endif()
+add_subdirectory (src)
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..aace2ff
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,426 @@
+v1.9.0-rc3
+============
+Bug #4132 - Laggy mouse cursor on macOS clients
+
+v1.9.0-rc2
+===========
+Bug #5901 - Stored serial key corrupted on macOS
+Bug #5757 - Failure to build against OpenSSL v1.1.0
+
+v1.9.0-rc1
+==========
+Bug #5467 - Failing to automatically download and install Bonjour
+Enhancement #5389 - Ported GUI to Qt 5
+Enhancement #4978 - Windows: Added support for Visual Studio 2015
+Enhancement #5398 - Windows: Updated OpenSSL dependency to 1.0.2k
+
+v1.8.8-stable
+==========
+Bug #5196 - Some keys on Korean and Japanese keyboards have the same keycode
+Bug #5578 - Pressing Hangul key results in alt+'a'
+Bug #5785 - Can't switch screens when cursor is in a corner
+Bug #3992 - macOS: Dragging is broken in Unity 3D
+Bug #5075 - macOS: Build fails on macOS 10.9 due to unknown compiler flag
+Bug #5809 - macOS: No version number is shown in the App Info dialog
+Bug #3197 - Linux: switchDoubleTap option is not working
+Bug #4477 - Linux: Mouse buttons higher than id 10 result in crash
+Bug #5832 - Linux: Screen size misdetected on multi-monitor display
+Enhancement #4504 - Improved Korean language description
+Enhancement #5525 - Added support for precise screen positioning in config file
+Enhancement #4290 - Windows: Removed annoying alt+print screen functionality
+
+v1.8.7-stable
+=============
+Bug #5784 - Edition changes when reopening GUI
+
+v1.8.6-stable
+=============
+Bug #5592 - Some keys don't work for macOS Sierra clients
+Bug #5186 - Cursor stuck on client when using multi-DPI server
+Bug #5722 - Malformed serial key in registry will crash GUI on startup
+Bug #5752 - Tab order is incorrect on Settings dialog
+Enhancement #5699 - Unified installers on macOS
+Feature #4836 - macOS Sierra build
+
+v1.8.5-stable
+=============
+Bug #5680 - Server crashes when disconnecting SSL clients
+Bug #5626 - Build fails using Xcode 8 and macOS SDK 10.12
+Feature #5657 - Trial version support
+Feature #5707 - User upgrade statistics
+
+v1.8.4-stable
+=============
+Bug #5183 - Slowly moving the cursor has no effect on high DPI clients
+Bug #4041 - UHD/4K DPI scaling broken on Windows servers
+Bug #4420 - When XRandR adds a screen, it is inaccessible
+Bug #5603 - Activation notification depends on existence of /etc/os-release
+Bug #5624 - Update notification sometimes requests a downgrade
+Bug #5329 - Current date is shown for build date in the about dialog
+Enhancement #5617 - Remove redundant plugin infrastructure
+Enhancement #5627 - Move SSL certificate generation to main window
+Enhancement #5628 - Move SSL implementation into core binary
+Enhancement #5629 - Move activation from wizard into new dialog window
+
+v1.8.3-stable
+=============
+Bug #2765 - A letter appears on macOS clients when the spacebar is pressed
+Bug #3241 - Windows UAC disconnects clients when elevated
+Bug #4740 - Linux client crashes with "Assertion '!m_open' failed"
+Bug #4879 - Memory leak caused by IpcReader
+Bug #5373 - Tab behaves like shift tab on client
+Bug #5502 - Copy and paste from server to client doesn't work
+Enhancement #123 - Option to disable clipboard sharing
+Enhancement #3305 - Media key support on macOS
+Enhancement #4323 - Make automatic elevation on Windows optional
+
+v1.8.2-stable
+=============
+Bug #3044 - Unable to drag-select in MS Office
+Bug #4768 - Copy paste causes 'server is dead' error on switching
+Bug #4792 - Server logging crashes when switching with clipboard data
+Bug #2975 - Middle click does not close Chrome tab on Mac client
+Bug #5087 - Linux client fails to start due to invalid cursor size
+Bug #5471 - Serial key textbox on activation screen overflows on Mac
+Bug #4836 - Stop button resets to Start when settings dialog canceled
+Enhancement #5277 - Auto restart service when synwinhk.dll fails on Windows
+Enhancement #4913 - Future-proof GUI login by using newer auth URL
+Enhancement #4922 - Add --enable-crypto argument to help text
+Enhancement #5299 - High resolution App icon on Mac
+Enhancement #4894 - Improve grammar in connection notification dialog
+
+v1.8.1-stable
+=============
+Bug #5461 - GUI crash during activation on Mac
+
+v1.8.0-beta
+=============
+Enhancement #4696 - Include 'ns' plugin in installers (instead of wizard download)
+Enhancement #4715 - Activation dialog which also accepts a serial key
+Enhancement #5020 - Recommend using serial key when online activation fails
+Enhancement #4893 - Show detailed version info on GUI about screen
+Enhancement #4327 - GUI setting to disable drag and drop feature
+Enhancement #4793 - Additional logging to output OpenSSL version
+Enhancement #4932 - Notify activation system when wizard finishes
+Enhancement #4716 - Allow software to be time limited with serial key
+
+v1.7.6-stable
+=============
+Bug #451 - Fast cursor on any client with Mac server
+Bug #5041 - Copying from the Chrome web browser doesn't work
+Bug #4735 - Clipboard doesn't work from client to server
+Bug #2909 - Clipboard copies only plaintext between Mac and Windows
+Bug #4353 - Large clipboard causes crash
+Bug #3774 - Missing MinGW dependencies after install on Windows
+Bug #4723 - Waiting for active desktop result freezes Windows service
+
+v1.7.5-stable
+=============
+Bug #5030 - Display scaling breaks edge detection on Windows
+Bug #5064 - Compile fails on Mac OS X 10.11 (unused typedef)
+
+v1.7.4-stable
+=============
+Bug #4721 - High CPU usage for Windows service
+Bug #4750 - SSL connect error 'passive ssl error limit'
+Bug #4584 - Drag and drop with SSL causes crash
+Bug #4749 - Clipboard thread race condition causes assertion failure
+Bug #4720 - Plugin download shows 'Could not get Linux package type' error
+Bug #4712 - Unable to send clipboard with size above 1KB when using SSL
+Bug #4642 - Connecting causes SSL23_GET_SERVER_HELLO error
+Bug #4690 - Log line 'activeDesktop' does not use logging system
+Bug #4866 - Wrong ns plugin version can be loaded
+Enhancement #4901 - Auto restart when running from GUI in desktop mode
+Enhancement #4845 - Add timestamp to log output
+Enhancement #4898 - Move version stage name to build config
+
+v1.7.3-stable
+=============
+Bug #4565 - Incorrect plugin downloads on Debian and Mint
+Bug #4677 - Windows service log file grows to very large size
+Bug #4651 - High logging rate causes Windows service to crash
+Bug #4650 - SSL error log message repeats excessively and freezes cursor
+Bug #4624 - Runaway logging causes GUI to freeze
+Bug #4617 - Windows service randomly stops after 'ssl handshake failure' error
+Bug #4601 - Large clipboard data with SSL causes 'protocol is shutdown' error
+Bug #4593 - Locking Windows server causes SSL_ERROR_SSL to repeat
+Bug #4577 - Memory leak in GUI on Windows caused by logging
+Bug #4538 - Windows service crashes intermittently with no error
+Bug #4341 - GUI freezes on first load when reading log
+Bug #4566 - Client or server crashes with 'ssl handshake failure' error
+Bug #4706 - Installer is not output to build config dir on Windows
+Bug #4704 - Plugin 'ns' release build is overwritten with debug version on Linux
+Bug #4703 - Plugins are not built to config directory on Mac
+Bug #4697 - Timing can allow an SSL socket to be used after cleanup call
+Enhancement #4661 - Log error but do not crash when failing to load plugins
+Enhancement #4708 - Download ns plugin for specific Mac versions
+Enhancement #4587 - Include OpenSSL binaries in source for easier building
+Enhancement #4695 - Automatically upload plugins as Buildbot step
+
+v1.7.2-stable
+=============
+Bug #4564 - Modifier keys often stuck down on Mac client
+Bug #4581 - Starting GUI on Mac crashes instantly on syntool segfault
+Bug #4520 - Laggy or sluggish cursor (ping spikes) on Mac when using WiFi
+Bug #4607 - GUI doesn't start after install on Windows
+Enhancement #4412 - Automate extract and compile for OpenSSL
+Enhancement #4567 - SSL plugin should use TLSv1_method() minimum
+Enhancement #4591 - Revert to legacy Mac deployment and signing
+Enhancement #4569 - Reintroduce GUI auto-hide setting (disabled by default)
+Enhancement #4570 - Make `--crypto-pass` show deprecated message
+Enhancement #4596 - Typo 'occurred' in WebClient.cpp
+
+v1.7.1-stable
+=============
+Bug #3784 - Double click & drag doesn't select words on client
+Bug #3052 - Triple-click (select line) does not work
+Bug #4367 - Duplicate Alt-S Keyboard Shortcuts on Gui
+Bug #4554 - Server unable to accept new SSL connection
+Bug #4553 - SSL handshake failure error causes GUI to crash
+Bug #4551 - Plugin wizard doesn't create SSL directory
+Bug #4548 - Severe code duplication in fingerprint logic
+Bug #4547 - Windows server crashes when client fingerprint dialog open
+Bug #4539 - Mac client dies when server has SSL_ERROR_SSL
+Bug #4537 - Plugin wizard doesn't complete but finish button enabled
+Bug #4535 - Server crashes on shut down after multiple connections failed
+Bug #4528 - Error SSL_ERROR_SSL is logged on unknown error
+Bug #4527 - Server fingerprint dialog on client GUI keeps showing
+Bug #4469 - GUI crashes on Windows when generating certificate
+Bug #4410 - SSL_ERROR_SSL (unknown protocol) on Mac client
+Bug #4409 - SSL_ERROR_SSL (unknown alert type) on Windows 8.1 client
+Bug #4557 - GUI doesn't show local fingerprint on fresh install
+Enhancement #4522 - SSL server fingerprint verification from client
+Enhancement #4526 - Display local fingerprint on server GUI
+Enhancement #4549 - Extract SSL certificate and fingerprint generate function
+Enhancement #4546 - Redistribute OpenSSL on Windows with installer
+Enhancement #4540 - Enable Network Security checkbox only when ns plugin exists
+Enhancement #4525 - Reorganize app data directory
+Enhancement #4390 - Disable GUI auto-hide by default
+
+v1.7.0-beta
+===========
+Enhancement #4313 - SSL encrypted secure connection
+Enhancement #4168 - Plugin manager for GUI
+Enhancement #4307 - Always show client auto-detect dialog
+Enhancement #4397 - Modernize Mac build script (deployment and signing)
+Enhancement #4398 - Remove obsolete Mac database cleaner
+Enhancement #4337 - Remove IStreamFilterFactory dead code
+
+1.6.3
+=====
+Bug #4349 - Mouse click does not always bring window to front
+Bug #4463 - Unidentified developer error on Mac OS X
+Bug #4464 - Code signing verify failure not reported on Mac build
+Bug #4465 - Binary (syntool) is not code signed on Windows
+Enhancement #4455 - Replace version with branch name in package filename
+
+1.6.2
+=====
+Bug #4227 - Helper tool crashes when service checks elevation state
+Bug #4091 - Zeroconf on server advertises bogus IP address
+Bug #4249 - Drag file causes client crash on Mac (10.10)
+Enhancement #4196 - Optional Bonjour requirement for Windows
+Enhancement #4235 - Automatic Bonjour download and install
+Enhancement #4218 - Auto-config available servers combo box
+Enhancement #4230 - More user friendly dialog when client is detected
+Enhancement #4240 - Minimize auto config message box usage
+Enhancement #4247 - Firewall exception for GUI (needed for Bonjour)
+Enhancement #4242 - Consistent naming for auto config feature
+
+1.6.1
+=====
+Bug #4002 - Carbon loop not ready within 5 sec
+Bug #4191 - Accessibility helper tool crashes
+Bug #4149 - Mac 10.9.5 or 10.10 gatekeeper blocks Synergy
+Bug #4139 - Exception thrown when ProcessIdToSessionId() fails
+Bug #4055 - Shift keys are not sent to clients (Win 8.1 server)
+Bug #4021 - Copy & paste not working for EFL applications
+Bug #3749 - Linux Chrome hover doesn't work
+Bug #4128 - Daemon logging not written with "log to file"
+Enhancement #4122 - Enable drag and drop by default
+Enhancement #4158 - Build for Mac OS X 10.10
+Enhancement #4130 - Auto elevate for Windows UAC and screen lock
+Enhancement #4126 - 64-bit support for OS X
+Enhancement #4141 - DMRM message support for μSynergy
+Enhancement #4124 - More robust argument parsing
+
+1.6.0
+=====
+Feature #65 - Auto config feature using Zeroconf/Bonjour
+
+1.5.1
+=====
+Bug #3307 - Configuration file paths containing spaces don't work
+Bug #3404 - Log path needs to be in quotes on windows
+Bug #3996 - Installer fails when Windows Firewall is disabled
+
+1.5.0
+=====
+Bug #4060 - Key stuck down on Windows server
+Bug #4061 - Windows server repeats modifier keys
+
+1.4.18
+======
+Bug #3980 - Shell extension DLL causes explorer.exe to crash
+Task #4049 - Correct code style in OSXKeyState compilation unit
+Task #4050 - Fix subversion issue tracker URL
+Task #4053 - Improve deb package quality
+Task #4054 - Improve rpm package quality
+
+1.4.17
+======
+Bug #2836 - Unable to begin screen name or alias with numbers
+Bug #3796 - Some files being unintentionally dragged (including explorer.exe)
+Bug #3886 - Alias is allowed to match screen name
+Bug #3919 - RPM install fails on Fedora 20, failed dependencies: libcurl
+Bug #3921 - Error: synwinxt.dll outdated (upgrading from 1.4.15 to 1.4.16)
+Bug #3927 - Mavericks accessibility exception not working (when upgrading from 1.4.15 to 1.4.16)
+Bug #3933 - Plus signs in the email address cause premium login to fail
+Bug #3939 - Compile fails on ARM (Raspberry Pi) because of cryptopp/Crypto++ lib
+Bug #3947 - Conflicts when using yum localinstall on Fedora 20
+Bug #3959 - Premium title doesn't always show on first login
+Bug #3968 - GUI auto-hides on initial first install (with no config)
+Task #3936 - Change installer to WiX for improved file upgrade process
+Task #3950 - Poll modifier after key down on Mac OS X and log results
+Task #3951 - Clear filename stored in synwinxt on mouse up
+Task #3952 - Make Premium wizard page cleaner
+Task #3953 - Inherit XArch and XBase from std::exception
+Task #3954 - Make "lock to screen" log message go to NOTE level instead of DEBUG
+Task #3960 - Split CMSWindowsHookLibraryLoader into hook and shellex loaders
+Task #3961 - Remove Windows 95 support
+Task #3963 - Disable failing Linux unit/integ tests on Fedora 20 32-bit (valgrind SIGILL)
+Task #3964 - Make Premium login error more verbose
+Task #3969 - Merge String.cpp and StringUtil.cpp
+
+1.4.16
+======
+Bug #3338 - Alt tab not working with Windows 8
+Bug #3642 - Failed to start server on Mac OS X 10.9 Mavericks, assistive devices problem
+Bug #3785 - Synwinxt.dll error opening file for writing during install of 1.4.15
+Bug #3787 - Wont automatically load after login on OS X
+Bug #3788 - Configuration wizard: Premium login fails when behind a proxy
+Bug #3796 - Some files being unintentionally dragged (including explorer.exe)
+Bug #3799 - Synergy Client on Fedora crashes on drag/drop operations
+Bug #3818 - Client freezes on Mac OS 10.6.8
+Bug #3874 - Premium GUI login is case sensitive for email
+Bug #3911 - Drag and drop error on OS X 10.9 Mavericks
+
+1.4.15
+======
+Bug #3765 - Synergy Service - Error 87: The parameter is incorrect.
+Bug #3781 - Option not supported on Linux: --enable-drag-drop (server not starting)
+
+1.4.14
+======
+Bug #3287 - Mac does not wake up
+Bug #3758 - Unstable service (synergyd)
+Bug #3759 - Exploit: C:\Program.exe (if it exists) is run by service (elevated)
+Bug #3760 - Encryption broken (GCM, CTR and OFB)
+Bug #3761 - Start button is visible when Synergy is running
+Bug #3762 - Apply button is disabled for Mac and Linux
+Feature #46 - Drag and drop between computers (Windows and Mac)
+
+1.4.13
+======
+Version not released, unstable.
+
+1.4.12
+======
+Bug #3565 - Encryption fails when typing fast (Invalid message from client)
+Bug #3606 - GUI is elevated after setup
+Bug #3572 - Mac caps lock causes disconnect
+
+1.4.11
+======
+Feature #12 - Encryption
+Feature #421 - Portable version
+Bug #2855 - Mouse cursor remains hidden on Mac client (intermittently/randomly)
+Bug #3281 - server start on OS X defaults to 'interactive'
+Bug #3310 - P&ort in settings screen
+
+1.4.10
+======
+Bug #2799 - Right shift broken (Windows server, Mac OS X client)
+Bug #3302 - GUI does not show/hide when tray icon is double clicked (Windows)
+Bug #3303 - Mac OS X IPC integ test fails intermittently
+Feature #2974 - Gesture Support for Magic Mouse/Trackpad
+Feature #3172 - Button to stop Synergy when in service mode
+Feature #3241 - Option to elevate synergyc/s when in service mode
+Feature #3242 - Show a list of available IP addresses and screen name on the main screen
+Feature #3296 - 64-bit Windows installer should display helpful message on 32-bit Windows
+Feature #3300 - Make service mode default mode (now that we have elevate option)
+Feature #3301 - Add process mode option to settings (remove startup wizard page)
+Feature #3306 - Gatekeeper compatibility on Mac OS X 10.8
+
+1.4.9
+=====
+Bug #3159 - In service mode, server doesn't start unless GUI is running
+Bug #3214 - Client sometimes can't connect if GUI is closed
+Bug #56 - Mac OS X server not sending keystrokes to client
+Bug #3161 - First time GUI appears, service doesn't send logging
+Bug #3164 - In service mode, you need to add a firewall exception
+Bug #3166 - Service shutdown stalls when GUI is closed
+Bug #3216 - Fatal error if plugins folder doesn't exist
+Bug #3221 - ERROR: could not connect to service, error: 2
+Feature #3192 - Add support for JOYINFOEX structure to poll game device info
+Feature #3202 - Plugin support (sending for primary screen events on Windows only)
+Feature #3155 - Cross-platform TCP IPC between GUI and service
+Task #3177 - Fix Mac buildslave to build multiple versions
+Task #3193 - Add Micro Synergy to repository
+Task #3275 - Change hostname label to "IP address or hostname"
+Task #3276 - Installation recovery mechanism for synrgyhk.dll
+
+1.4.8
+=====
+Bug #143: Cursor on Mac OS X goes to center when inactive
+Bug #146: Screen Resize causes problems with moving off right-hand side of screen
+Bug #3058: Modifier keys not working on Mac OS X server
+Bug #3139: Double click too strict (click, move, click should not count)
+Bug #3195: Service install can fail first time
+Bug #3196: Wizard buttons not visible
+Bug #3197: GUI doesn't take focus after install
+Bug #3202: Hook DLL (synrgyhk.dll) is not released
+Feature #3143: Setup wizard for first time users
+Feature #3145: Check for updates
+Feature #3174: Startup mode wizard page
+Feature #3184: New service for process management
+
+1.4.7
+=====
+Bug #3132: GUI hides before successful connection
+Bug #3133: Can't un-hide GUI on Mac
+Feature #3054: Hide synergy[cs] dock icon (Mac OS X)
+Feature #3135: Integrate log into main window
+Task #3134: Move hotkey warnings to DEBUG
+
+1.4.6
+=====
+Bug #155: Build error on FreeBSD (missing sentinel in function call)
+Bug #571: Synergy SegFaults with "Unknown Quartz Event type: 0x1d"
+Bug #617: xrandr rotation on client confines cursor in wrong area
+Bug #642: `synergyc --help` segfaults on sparc64 architecture
+Bug #652: Stack overflow in getIDForKey
+Bug #1071: Can't copy from the Firefox address bar on Linux
+Bug #1662: Copying text from remote computer crashes java programs.
+Bug #1731: YouTube can cause server to freeze randomly
+Bug #2752: Use SAS for ctrl+alt+del on win7
+Bug #2763: Double-click broken on Mac OS
+Bug #2817: Keypad Subtract has wrong keycode on OS X
+Bug #2958: GNOME 3 mouse problem (gnome-shell)
+Bug #2962: Clipboard not working on mac client
+Bug #3063: Segfault in copy buffer
+Bug #3066: Server segfault on clipboard paste
+Bug #3089: Comma and Period translated wrong when using the NEO2-layout
+Bug #3092: Wrong screen rotation detected
+Bug #3105: There doesn't seem to be a system tray available. Quitting
+Bug #3116: Memory Leak due to the XInput2 patches
+Bug #3117: Dual monitors not detected properly anymore
+Feature #3073: Re-introduce auto-start GUI (Windows)
+Feature #3076: Re-introduce auto-start backend
+Feature #3077: Re-introduce hidden on start
+Feature #3091: Add option to remap altgr modifier
+Feature #3119: Mac OS X secondary screen
+Task #2905: Unit tests: Clipboard classes
+Task #3072: Downgrade Linux build machines
+Task #3090: CXWindowsKeyState integ test args wrong
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..acfef1b
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,288 @@
+Copyright (C) 2018 Debauchee Open Source Group
+Copyright (C) 2012-2016 Symless Ltd.
+Copyright (C) 2008-2014 Nick Bolton
+Copyright (C) 2002-2014 Chris Schoeneman
+
+This program is released under the GPL with the additional exemption
+that compiling, linking, and/or using OpenSSL is allowed.
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fe10138
--- /dev/null
+++ b/README.md
@@ -0,0 +1,31 @@
+# Barrier
+
+Eliminate the barrier between your machines.
+
+Master branch build status: &nbsp; [![Build Status](https://travis-ci.org/debauchee/barrier.svg?branch=master)](https://travis-ci.org/debauchee/barrier)
+
+### What is it?
+
+Barrier is KVM software forked from Symless's synergy 1.9 codebase. Synergy was a commercialized reimplementation of the original CosmoSynergy written by Chris Schoeneman.
+
+### What's different?
+
+Whereas synergy has moved beyond its goals from the 1.x era, barrier aims to maintain that simplicity. Barrier will let you use your keyboard and mouse from machine A to control machine B (or more). That's it.
+
+### Project goals
+
+Hassle-free reliability. We are users, too. Barrier was created so that we could solve the issues we had with synergy and then share these fixes with other users.
+
+Compatibility. We use more than one operating system and you probably do, too. Windows, OSX, Linux, FreeBSD... Barrier should "just work". We will also have our eye on Wayland when the time comes.
+
+Communication. Everything we do is in the open. Our issue tracker will let you see if others are having the same problem you're having and will allow you to add additional information. You will also be able to see when progress is made and how the issue gets resolved.
+
+### Contact & support
+
+Please be aware that the *only* way to draw our attention to a bug is to create a new PR in the issue tracker. Write a clear, concise, detailed report and you will get a clear, concise, detailed response. Priority is always give to issues that affect a wider range of users.
+
+For short and simple questions or to just say hello find us on the Freenode IRC network in the #barrier channel.
+
+### Contributions
+
+At this time we are looking for developers to help fix the issues found in the issue tracker. Submit pull requests once you've polished up your patch and we'll review and possibly merge it.
diff --git a/build_installer.bat b/build_installer.bat
new file mode 100644
index 0000000..424e585
--- /dev/null
+++ b/build_installer.bat
@@ -0,0 +1,35 @@
+@echo off
+set WIX_ROOT=C:\Program Files (x86)\WiX Toolset v3.11
+
+set savedir=%cd%
+cd /d %~dp0
+
+if not exist build\bin\Release goto buildproject
+
+cd build\installer
+if ERRORLEVEL 1 goto buildproject
+
+echo Building 64-bit Windows installer...
+"%WIX_ROOT%\bin\candle.exe" -nologo -arch x64 -dConfiguration=Release -dPlatform=x64 -ext WixUtilExtension -ext WixFirewallExtension Product.wxs -o Barrier.wixobj
+if ERRORLEVEL 1 goto failed
+"%WIX_ROOT%\bin\light.exe" -nologo -ext WixUtilExtension -ext WixFirewallExtension -ext WixUIExtension Barrier.wixobj -o bin\Barrier.msi
+if ERRORLEVEL 1 goto failed
+echo Build completed successfully
+goto done
+
+:buildproject
+echo To build a 64-bit Windows installer:
+echo - set Q_BUILD_TYPE=Release in build_env.bat
+echo - also set other environmental overrides necessary for your build environment
+echo - run clean_build.bat to build Barrier and verify that it succeeds
+echo - re-run this script to create the installation package
+goto done
+
+:failed
+echo Build failed
+
+:done
+set WIX_ROOT=
+
+cd /d %savedir%
+set savedir=
diff --git a/clean_build.bat b/clean_build.bat
new file mode 100644
index 0000000..c5e2153
--- /dev/null
+++ b/clean_build.bat
@@ -0,0 +1,71 @@
+@echo off
+
+REM defaults - override them by creating a build_env.bat file
+set B_BUILD_TYPE=Debug
+set B_QT_ROOT=C:\Qt
+set B_QT_VER=5.6.3
+set B_QT_MSVC=msvc2015_64
+set B_BONJOUR=C:\Program Files\Bonjour SDK
+
+set savedir=%cd%
+cd /d %~dp0
+
+if exist build_env.bat call build_env.bat
+
+REM needed by cmake to set bonjour include dir
+set BONJOUR_SDK_HOME=%B_BONJOUR%
+
+REM full path to Qt stuff we need
+set B_QT_FULLPATH=%B_QT_ROOT%\%B_QT_VER%\%B_QT_MSVC%
+
+echo Bonjour: %BONJOUR_SDK_HOME%
+echo Qt: %B_QT_FULLPATH%
+
+rmdir /q /s build
+mkdir build
+if ERRORLEVEL 1 goto failed
+cd build
+cmake -G "Visual Studio 15 2017 Win64" -D CMAKE_BUILD_TYPE=%B_BUILD_TYPE% -D CMAKE_PREFIX_PATH="%B_QT_FULLPATH%" -D DNSSD_LIB="%B_BONJOUR%\Lib\x64\dnssd.lib" -D QT_VERSION=%B_QT_VER% ..
+if ERRORLEVEL 1 goto failed
+echo @msbuild barrier.sln /p:Platform="x64" /p:Configuration=%B_BUILD_TYPE% /m %B_BUILD_OPTIONS% > make.bat
+call make.bat
+if ERRORLEVEL 1 goto failed
+if exist bin\Debug (
+ copy %B_QT_FULLPATH%\bin\Qt5Cored.dll bin\Debug\ > NUL
+ copy %B_QT_FULLPATH%\bin\Qt5Guid.dll bin\Debug\ > NUL
+ copy %B_QT_FULLPATH%\bin\Qt5Networkd.dll bin\Debug\ > NUL
+ copy %B_QT_FULLPATH%\bin\Qt5Widgetsd.dll bin\Debug\ > NUL
+ copy %B_QT_FULLPATH%\bin\Qt5Cored.dll bin\Debug\ > NUL
+ copy ..\ext\openssl\windows\x64\bin\* bin\Debug\ > NUL
+ copy ..\res\openssl\barrier.conf bin\Debug\ > NUL
+) else if exist bin\Release (
+ copy %B_QT_FULLPATH%\bin\Qt5Core.dll bin\Release\ > NUL
+ copy %B_QT_FULLPATH%\bin\Qt5Gui.dll bin\Release\ > NUL
+ copy %B_QT_FULLPATH%\bin\Qt5Network.dll bin\Release\ > NUL
+ copy %B_QT_FULLPATH%\bin\Qt5Widgets.dll bin\Release\ > NUL
+ copy %B_QT_FULLPATH%\bin\Qt5Core.dll bin\Release\ > NUL
+ copy ..\ext\openssl\windows\x64\bin\* bin\Release\ > NUL
+ copy ..\res\openssl\barrier.conf bin\Release\ > NUL
+ mkdir bin\Release\platforms
+ copy %B_QT_FULLPATH%\plugins\platforms\qwindows.dll bin\Release\platforms\ > NUL
+) else (
+ echo Remember to copy supporting binaries and confiuration files!
+)
+
+echo Build completed successfully
+goto done
+
+:failed
+echo Build failed
+
+:done
+cd /d %savedir%
+
+set B_BUILD_TYPE=
+set B_QT_ROOT=
+set B_QT_VER=
+set B_QT_MSVC=
+set B_BONJOUR=
+set BONJOUR_SDK_HOME=
+set B_QT_FULLPATH=
+set savedir=
diff --git a/clean_build.sh b/clean_build.sh
new file mode 100755
index 0000000..3085c08
--- /dev/null
+++ b/clean_build.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+cd "$(dirname $0)" || exit 1
+# some environments have cmake v2 as 'cmake' and v3 as 'cmake3'
+# check for cmake3 first then fallback to just cmake
+B_CMAKE=`type cmake3 2>/dev/null`
+if [ $? -eq 0 ]; then
+ B_CMAKE=`echo $B_CMAKE | cut -d' ' -f3`
+else
+ B_CMAKE=cmake
+fi
+# default build configuration
+B_BUILD_TYPE=${B_BUILD_TYPE:-Debug}
+if [ "$(uname)" = "Darwin" ]; then
+ # OSX needs a lot of extra help, poor thing
+ # run the osx_environment.sh script to fix paths
+ . ./osx_environment.sh
+ B_CMAKE_FLAGS="-DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=10.9 $B_CMAKE_FLAGS"
+fi
+# allow local customizations to build environment
+[ -r ./build_env.sh ] && . ./build_env.sh
+B_CMAKE_FLAGS="-DCMAKE_BUILD_TYPE=$B_BUILD_TYPE $B_CMAKE_FLAGS"
+rm -rf build
+mkdir build || exit 1
+cd build || exit 1
+echo Starting Barrier $B_BUILD_TYPE build...
+$B_CMAKE $B_CMAKE_FLAGS .. || exit 1
+make || exit 1
+echo "Build completed successfully"
diff --git a/cmake/Package.cmake b/cmake/Package.cmake
new file mode 100644
index 0000000..da65de7
--- /dev/null
+++ b/cmake/Package.cmake
@@ -0,0 +1,9 @@
+if (UNIX)
+ set (CPACK_PACKAGE_VERSION_MAJOR ${BARRIER_VERSION_MAJOR})
+ set (CPACK_PACKAGE_VERSION_MINOR ${BARRIER_VERSION_MINOR})
+ set (CPACK_PACKAGE_VERSION_PATCH ${BARRIER_VERSION_PATCH})
+ set (CPACK_GENERATOR "TBZ2")
+ set (CPACK_SOURCE_GENERATOR "TXZ")
+ set (CPACK_SOURCE_IGNORE_FILES "/build/;\.gitignore$;/\.git/;/.github/;\.swp$;build_env\.*")
+ include (CPack)
+endif()
diff --git a/cmake/Version.cmake b/cmake/Version.cmake
new file mode 100644
index 0000000..2f38df2
--- /dev/null
+++ b/cmake/Version.cmake
@@ -0,0 +1,94 @@
+cmake_minimum_required (VERSION 3.4)
+
+set (BARRIER_VERSION_MAJOR 2)
+set (BARRIER_VERSION_MINOR 0)
+set (BARRIER_VERSION_PATCH 0)
+
+#
+# Barrier Version
+#
+if (NOT DEFINED BARRIER_VERSION_MAJOR)
+ if (DEFINED ENV{BARRIER_VERSION_MAJOR})
+ set (BARRIER_VERSION_MAJOR $ENV{BARRIER_VERSION_MAJOR})
+ else()
+ set (BARRIER_VERSION_MAJOR 1)
+ endif()
+endif()
+
+if (NOT DEFINED BARRIER_VERSION_MINOR)
+ if (DEFINED ENV{BARRIER_VERSION_MINOR})
+ set (BARRIER_VERSION_MINOR $ENV{BARRIER_VERSION_MINOR})
+ else()
+ set (BARRIER_VERSION_MINOR 9)
+ endif()
+endif()
+
+if (NOT DEFINED BARRIER_VERSION_PATCH)
+ if (DEFINED ENV{BARRIER_VERSION_PATCH})
+ set (BARRIER_VERSION_PATCH $ENV{BARRIER_VERSION_PATCH})
+ else()
+ set (BARRIER_VERSION_PATCH 0)
+ message (WARNING "Barrier version wasn't set. Set to ${BARRIER_VERSION_MAJOR}.${BARRIER_VERSION_MINOR}.${BARRIER_VERSION_PATCH}")
+ endif()
+endif()
+
+if (NOT DEFINED BARRIER_VERSION_STAGE)
+ if (DEFINED ENV{BARRIER_VERSION_STAGE})
+ set (BARRIER_VERSION_STAGE $ENV{BARRIER_VERSION_STAGE})
+ else()
+ set (BARRIER_VERSION_STAGE "snapshot")
+ endif()
+endif()
+
+if (NOT DEFINED BARRIER_REVISION)
+ if (DEFINED ENV{GIT_COMMIT})
+ string (SUBSTRING $ENV{GIT_COMMIT} 0 8 BARRIER_REVISION)
+ else()
+ execute_process (
+ COMMAND git rev-parse --short=8 HEAD
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_VARIABLE BARRIER_REVISION
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+ endif()
+endif()
+
+if (DEFINED BARRIER_REVISION)
+ string(LENGTH ${BARRIER_REVISION} BARRIER_REVISION_LENGTH)
+ if (NOT ((BARRIER_REVISION MATCHES "^[a-f0-9]+") AND (BARRIER_REVISION_LENGTH EQUAL "8")))
+ message (FATAL_ERROR "BARRIER_REVISION ('${BARRIER_REVISION}') should be a short commit hash")
+ endif()
+ unset (BARRIER_REVISION_LENGTH)
+else()
+ set (BARRIER_REVISION "0badc0de")
+endif()
+
+if (DEFINED ENV{BUILD_NUMBER})
+ set (BARRIER_BUILD_NUMBER $ENV{BUILD_NUMBER})
+else()
+ set (BARRIER_BUILD_NUMBER 1)
+endif()
+
+string (TIMESTAMP BARRIER_BUILD_DATE "%Y%m%d" UTC)
+set (BARRIER_SNAPSHOT_INFO ".${BARRIER_VERSION_STAGE}.${BARRIER_REVISION}")
+
+if (BARRIER_VERSION_STAGE STREQUAL "snapshot")
+ set (BARRIER_VERSION_TAG "${BARRIER_VERSION_STAGE}.b${BARRIER_BUILD_NUMBER}-${BARRIER_REVISION}")
+else()
+ set (BARRIER_VERSION_TAG "${BARRIER_VERSION_STAGE}")
+endif()
+
+set (BARRIER_VERSION "${BARRIER_VERSION_MAJOR}.${BARRIER_VERSION_MINOR}.${BARRIER_VERSION_PATCH}-${BARRIER_VERSION_STAGE}")
+set (BARRIER_VERSION_STRING "${BARRIER_VERSION}-${BARRIER_VERSION_TAG}")
+message (STATUS "Full Barrier version string is '" ${BARRIER_VERSION_STRING} "'")
+
+add_definitions (-DBARRIER_VERSION="${BARRIER_VERSION}")
+add_definitions (-DBARRIER_VERSION_STRING="${BARRIER_VERSION_STRING}")
+add_definitions (-DBARRIER_REVISION="${BARRIER_REVISION}")
+add_definitions (-DBARRIER_BUILD_DATE="${BARRIER_BUILD_DATE}")
+add_definitions (-DBARRIER_BUILD_NUMBER=${BARRIER_BUILD_NUMBER})
+
+if (BARRIER_DEVELOPER_MODE)
+ add_definitions (-DBARRIER_DEVELOPER_MODE=1)
+endif()
+
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..ec63514
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+9
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..192dff0
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,17 @@
+Source: barrier
+Section: utils
+Priority: optional
+Standards-Version: 3.9.7
+Homepage: https://github.com/debauchee/barrier/
+Maintainer: Debauchee Open Source Group <todo@mail.com>
+
+Package: barrier
+Architecture: amd64
+Section: utils
+Priority: optional
+Depends: ${shlibs:Depends},
+ ${misc:Depends}
+Description: Keyboard and mouse sharing solution
+ Barrier allows you to share one mouse and keyboard between multiple computers.
+ Work seamlessly across Windows, macOS and Linux.
+Homepage: https://github.com/debauchee/barrier/
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..d448129
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,5 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: Barrier
+Source: https://github.com/debauchee/barrier/
+Disclaimer: This package is not part of the Debian project as it contains closed source proprietary components
+Copyright: Copyright (C) 2018 Debauchee Open Source Group
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..64ddd45
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,5 @@
+#!/usr/bin/make -f
+
+%:
+ dh $@ --buildsystem=cmake --builddirectory=build -Pbuild/debian --parallel
+
diff --git a/debian/source/format b/debian/source/format
new file mode 100644
index 0000000..89ae9db
--- /dev/null
+++ b/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/dist/inno/barrier.iss.in b/dist/inno/barrier.iss.in
new file mode 100644
index 0000000..6b52bcc
--- /dev/null
+++ b/dist/inno/barrier.iss.in
@@ -0,0 +1,74 @@
+#define MyAppName "Barrier"
+#define MyAppVersion "@BARRIER_VERSION@"
+#define MyAppPublisher "Debauchee Open Source Group"
+#define MyAppURL "https://github.com/debauchee/barrier/wiki"
+#define MyAppExeName "barrier.exe"
+#define MyAppServiceName "Barrier"
+#define MyAppServiceExe "barrierd.exe"
+#define MyAppServiceDesc "Manages the Barrier background processes."
+
+[Setup]
+AppId={{41036EA6-3F7A-4803-8AE0-469E5E91EFCC}
+AppName={#MyAppName}
+AppVersion={#MyAppVersion}
+AppVerName={#MyAppName} {#MyAppVersion}
+AppPublisher={#MyAppPublisher}
+AppPublisherURL={#MyAppURL}
+AppSupportURL={#MyAppURL}
+AppUpdatesURL={#MyAppURL}
+DefaultDirName={pf}\{#MyAppName}
+DisableProgramGroupPage=yes
+LicenseFile=@CMAKE_CURRENT_SOURCE_DIR@/res/License.rtf
+OutputDir=@CMAKE_RUNTIME_OUTPUT_DIRECTORY@/../installer-inno/bin
+OutputBaseFilename=BarrierSetup-{#MyAppVersion}
+SetupIconFile=@CMAKE_CURRENT_SOURCE_DIR@/res/barrier.ico
+Compression=lzma
+SolidCompression=yes
+ArchitecturesInstallIn64BitMode=x64 ia64
+
+#include "scripts\lang\english.iss"
+
+[Tasks]
+Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
+
+[Files]
+Source: "@CMAKE_RUNTIME_OUTPUT_DIRECTORY@/Release/*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+; NOTE: Don't use "Flags: ignoreversion" on any shared system files
+
+[Icons]
+Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
+Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
+
+[Run]
+Filename: {sys}\sc.exe; Parameters: "create {#MyAppServiceName} start= auto binPath= ""{app}\{#MyAppServiceExe}"""; Flags: runhidden
+Filename: {sys}\sc.exe; Parameters: "description {#MyAppServiceName} ""{#MyAppServiceDesc}"""; Flags: runhidden
+Filename: {sys}\sc.exe; Parameters: "start {#MyAppServiceName}"; Flags: runhidden
+Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
+
+[UninstallDelete]
+Type: files; Name: "{app}\barrierd.log"
+
+[UninstallRun]
+Filename: {sys}\sc.exe; Parameters: "stop {#MyAppServiceName}"; Flags: runhidden
+Filename: {sys}\sc.exe; Parameters: "delete {#MyAppServiceName}"; Flags: runhidden
+
+[CustomMessages]
+DependenciesDir="redist"
+
+; shared code for installing the products
+#include "scripts\products.iss"
+#include "scripts\products\stringversion.iss"
+#include "scripts\products\winversion.iss"
+#include "scripts\products\msiproduct.iss"
+#include "scripts\products\vcredist2017.iss"
+
+[Code]
+function InitializeSetup(): boolean;
+begin
+ // initialize windows version
+ initwinversion();
+
+ vcredist2017('14'); // min allowed version is 14.0
+
+ Result := true;
+end;
diff --git a/dist/inno/scripts/isxdl/english.ini b/dist/inno/scripts/isxdl/english.ini
new file mode 100644
index 0000000..0546ae4
--- /dev/null
+++ b/dist/inno/scripts/isxdl/english.ini
@@ -0,0 +1,49 @@
+[strings]
+; General
+100=File download
+101=Do you want to cancel the download?
+102=%1 (%2 of %3)
+103=%1 KB
+104=%1 KB of %2 KB (%3%)
+
+; Status information
+110=Getting file information...
+111=Redirecting to %1
+112=Sending request...
+113=Resolving %1
+114=Connected to %1
+115=Receiving...
+116=Connecting to %1
+
+; Error messages
+120=Error connecting to the internet.\n\n%1
+121=Error opening %1.\n\nThe server returned status code %2.
+122=Error reading URL.\n\n%1
+123=Error writing file %1.\n\n%2
+124=Error opening file %1.\n\n%2
+125='%1' is an invalid URL.
+126=Error opening %1.\n\n%2
+127=Error sending request.\n\n%1
+128=Unsupported protocol. Only HTTP and FTP protocols are supported.
+129=Failed to connect to %1.\n\n%2
+130=Failed to query status code.\n\n%1
+131=Error requesting file.\n\n%1
+
+; Other
+144=About...
+146=Download
+147=Setup is now downloading additional files to your computer.
+
+; labels
+160=File:
+161=Speed:
+162=Status:
+163=Elapsed Time:
+164=Remaining Time:
+165=Current File:
+166=Overall Progress:
+167=Cancel
+168=OK
+169=User Name and Password
+170=User Name:
+171=Password:
diff --git a/dist/inno/scripts/isxdl/isxdl.dll b/dist/inno/scripts/isxdl/isxdl.dll
new file mode 100644
index 0000000..d227bca
--- /dev/null
+++ b/dist/inno/scripts/isxdl/isxdl.dll
Binary files differ
diff --git a/dist/inno/scripts/isxdl/isxdl.iss b/dist/inno/scripts/isxdl/isxdl.iss
new file mode 100644
index 0000000..3c25d6d
--- /dev/null
+++ b/dist/inno/scripts/isxdl/isxdl.iss
@@ -0,0 +1,14 @@
+[Files]
+Source: "scripts\isxdl\isxdl.dll"; Flags: dontcopy
+
+[Code]
+procedure isxdl_AddFile(URL, Filename: PAnsiChar);
+external 'isxdl_AddFile@files:isxdl.dll stdcall';
+
+function isxdl_DownloadFiles(hWnd: Integer): Integer;
+external 'isxdl_DownloadFiles@files:isxdl.dll stdcall';
+
+function isxdl_SetOption(Option, Value: PAnsiChar): Integer;
+external 'isxdl_SetOption@files:isxdl.dll stdcall';
+
+[Setup]
diff --git a/dist/inno/scripts/lang/english.iss b/dist/inno/scripts/lang/english.iss
new file mode 100644
index 0000000..fa32fc0
--- /dev/null
+++ b/dist/inno/scripts/lang/english.iss
@@ -0,0 +1,18 @@
+[Languages]
+Name: "en"; MessagesFile: "compiler:Default.isl"
+
+[CustomMessages]
+;http://www.microsoft.com/globaldev/reference/lcid-all.mspx
+en.lcid=1033
+en.depdownload_msg=The following applications are required before setup can continue:%n%n%1%nDownload and install now?
+en.depdownload_memo_title=Download dependencies
+en.depinstall_memo_title=Install dependencies
+en.depinstall_title=Installing dependencies
+en.depinstall_description=Please wait while Setup installs dependencies on your computer.
+en.depinstall_status=Installing %1...
+en.depinstall_missing=%1 must be installed before setup can continue. Please install %1 and run Setup again.
+en.depinstall_error=An error occured while installing the dependencies. Please restart the computer and run the setup again or install the following dependencies manually:%n
+
+en.isxdl_langfile=
+
+[Files]
diff --git a/dist/inno/scripts/products.iss b/dist/inno/scripts/products.iss
new file mode 100644
index 0000000..63cf4d1
--- /dev/null
+++ b/dist/inno/scripts/products.iss
@@ -0,0 +1,6 @@
+#include "isxdl\isxdl.iss"
+
+[Code]
+#include "products.pas"
+
+[Setup]
diff --git a/dist/inno/scripts/products.pas b/dist/inno/scripts/products.pas
new file mode 100644
index 0000000..d114c41
--- /dev/null
+++ b/dist/inno/scripts/products.pas
@@ -0,0 +1,329 @@
+{
+ --- TYPES AND VARIABLES ---
+}
+type
+ TProduct = record
+ File: String;
+ Title: String;
+ Parameters: String;
+ ForceSuccess : boolean;
+ InstallClean : boolean;
+ MustRebootAfter : boolean;
+ end;
+
+ InstallResult = (InstallSuccessful, InstallRebootRequired, InstallError);
+
+var
+ installMemo, downloadMessage: string;
+ products: array of TProduct;
+ delayedReboot, isForcedX86: boolean;
+ DependencyPage: TOutputProgressWizardPage;
+
+procedure AddProduct(filename, parameters, title, size, url: string; forceSuccess, installClean, mustRebootAfter : boolean);
+{
+ Adds a product to the list of products to download.
+ Parameters:
+ filename: the file name under which to save the file
+ parameters: the parameters with which to run the file
+ title: the product title
+ size: the file size
+ url: the URL to download from
+ forceSuccess: whether to continue in case of setup failure
+ installClean: whether the product needs a reboot before installing
+ mustRebootAfter: whether the product needs a reboot after installing
+}
+var
+ path: string;
+ i: Integer;
+begin
+ installMemo := installMemo + '%1' + title + #13;
+
+ path := ExpandConstant('{src}{\}') + CustomMessage('DependenciesDir') + '\' + filename;
+ if not FileExists(path) then begin
+ path := ExpandConstant('{tmp}{\}') + filename;
+
+ if not FileExists(path) then begin
+ isxdl_AddFile(url, path);
+
+ downloadMessage := downloadMessage + '%1' + title + ' (' + size + ')' + #13;
+ end;
+ end;
+
+ i := GetArrayLength(products);
+ SetArrayLength(products, i + 1);
+ products[i].File := path;
+ products[i].Title := title;
+ products[i].Parameters := parameters;
+ products[i].ForceSuccess := forceSuccess;
+ products[i].InstallClean := installClean;
+ products[i].MustRebootAfter := mustRebootAfter;
+end;
+
+function SmartExec(product : TProduct; var resultcode : Integer): boolean;
+{
+ Executes a product and returns the exit code.
+ Parameters:
+ product: the product to install
+ resultcode: the exit code
+}
+begin
+ if (LowerCase(Copy(product.File, Length(product.File) - 2, 3)) = 'exe') then begin
+ Result := Exec(product.File, product.Parameters, '', SW_SHOWNORMAL, ewWaitUntilTerminated, resultcode);
+ end else begin
+ Result := ShellExec('', product.File, product.Parameters, '', SW_SHOWNORMAL, ewWaitUntilTerminated, resultcode);
+ end;
+end;
+
+function PendingReboot: boolean;
+{
+ Checks whether the machine has a pending reboot.
+}
+var names: String;
+begin
+ if (RegQueryMultiStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager', 'PendingFileRenameOperations', names)) then begin
+ Result := true;
+ end else if ((RegQueryMultiStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager', 'SetupExecute', names)) and (names <> '')) then begin
+ Result := true;
+ end else begin
+ Result := false;
+ end;
+end;
+
+function InstallProducts: InstallResult;
+{
+ Installs the downloaded products
+}
+var
+ resultCode, i, productCount, finishCount: Integer;
+begin
+ Result := InstallSuccessful;
+ productCount := GetArrayLength(products);
+
+ if productCount > 0 then begin
+ DependencyPage := CreateOutputProgressPage(CustomMessage('depinstall_title'), CustomMessage('depinstall_description'));
+ DependencyPage.Show;
+
+ for i := 0 to productCount - 1 do begin
+ if (products[i].InstallClean and (delayedReboot or PendingReboot())) then begin
+ Result := InstallRebootRequired;
+ break;
+ end;
+
+ DependencyPage.SetText(FmtMessage(CustomMessage('depinstall_status'), [products[i].Title]), '');
+ DependencyPage.SetProgress(i, productCount);
+
+ while true do begin
+ // set 0 as used code for shown error if SmartExec fails
+ resultCode := 0;
+ if SmartExec(products[i], resultCode) then begin
+ // setup executed; resultCode contains the exit code
+ if (products[i].MustRebootAfter) then begin
+ // delay reboot after install if we installed the last dependency anyways
+ if (i = productCount - 1) then begin
+ delayedReboot := true;
+ end else begin
+ Result := InstallRebootRequired;
+ end;
+ break;
+ end else if (resultCode = 0) or (products[i].ForceSuccess) then begin
+ finishCount := finishCount + 1;
+ break;
+ end else if (resultCode = 3010) then begin
+ // Windows Installer resultCode 3010: ERROR_SUCCESS_REBOOT_REQUIRED
+ delayedReboot := true;
+ finishCount := finishCount + 1;
+ break;
+ end;
+ end;
+
+ case MsgBox(FmtMessage(SetupMessage(msgErrorFunctionFailed), [products[i].Title, IntToStr(resultCode)]), mbError, MB_ABORTRETRYIGNORE) of
+ IDABORT: begin
+ Result := InstallError;
+ break;
+ end;
+ IDIGNORE: begin
+ break;
+ end;
+ end;
+ end;
+
+ if Result <> InstallSuccessful then begin
+ break;
+ end;
+ end;
+
+ // only leave not installed products for error message
+ for i := 0 to productCount - finishCount - 1 do begin
+ products[i] := products[i+finishCount];
+ end;
+ SetArrayLength(products, productCount - finishCount);
+
+ DependencyPage.Hide;
+ end;
+end;
+
+{
+ --------------------
+ INNO EVENT FUNCTIONS
+ --------------------
+}
+
+function PrepareToInstall(var NeedsRestart: boolean): String;
+{
+ Before the "preparing to install" page.
+ See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents
+}
+var
+ i: Integer;
+ s: string;
+begin
+ delayedReboot := false;
+
+ case InstallProducts() of
+ InstallError: begin
+ s := CustomMessage('depinstall_error');
+
+ for i := 0 to GetArrayLength(products) - 1 do begin
+ s := s + #13 + ' ' + products[i].Title;
+ end;
+
+ Result := s;
+ end;
+ InstallRebootRequired: begin
+ Result := products[0].Title;
+ NeedsRestart := true;
+
+ // write into the registry that the installer needs to be executed again after restart
+ RegWriteStringValue(HKEY_CURRENT_USER, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', 'InstallBootstrap', ExpandConstant('{srcexe}'));
+ end;
+ end;
+end;
+
+function NeedRestart : boolean;
+{
+ Checks whether a restart is needed at the end of install
+ See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents
+}
+begin
+ Result := delayedReboot;
+end;
+
+function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
+{
+ Just before the "ready" page.
+ See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents
+}
+var
+ s: string;
+begin
+ if downloadMessage <> '' then
+ s := s + CustomMessage('depdownload_memo_title') + ':' + NewLine + FmtMessage(downloadMessage, [Space]) + NewLine;
+ if installMemo <> '' then
+ s := s + CustomMessage('depinstall_memo_title') + ':' + NewLine + FmtMessage(installMemo, [Space]) + NewLine;
+
+ if MemoDirInfo <> '' then
+ s := s + MemoDirInfo + NewLine + NewLine;
+ if MemoGroupInfo <> '' then
+ s := s + MemoGroupInfo + NewLine + NewLine;
+ if MemoTasksInfo <> '' then
+ s := s + MemoTasksInfo;
+
+ Result := s
+end;
+
+function NextButtonClick(CurPageID: Integer): boolean;
+{
+ At each "next" button click
+ See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents
+}
+begin
+ Result := true;
+
+ if CurPageID = wpReady then begin
+ if downloadMessage <> '' then begin
+ // change isxdl language only if it is not english because isxdl default language is already english
+ if (ActiveLanguage() <> 'en') then begin
+ ExtractTemporaryFile(CustomMessage('isxdl_langfile'));
+ isxdl_SetOption('language', ExpandConstant('{tmp}{\}') + CustomMessage('isxdl_langfile'));
+ end;
+ //isxdl_SetOption('title', FmtMessage(SetupMessage(msgSetupWindowTitle), [CustomMessage('appname')]));
+
+ //if SuppressibleMsgBox(FmtMessage(CustomMessage('depdownload_msg'), [FmtMessage(downloadMessage, [''])]), mbConfirmation, MB_YESNO, IDYES) = IDNO then
+ // Result := false
+ //else if
+ if isxdl_DownloadFiles(StrToInt(ExpandConstant('{wizardhwnd}'))) = 0 then
+ Result := false;
+ end;
+ end;
+end;
+
+{
+ -----------------------------
+ ARCHITECTURE HELPER FUNCTIONS
+ -----------------------------
+}
+
+function IsX86: boolean;
+{
+ Gets whether the computer is x86 (32 bits).
+}
+begin
+ Result := isForcedX86 or (ProcessorArchitecture = paX86) or (ProcessorArchitecture = paUnknown);
+end;
+
+function IsX64: boolean;
+{
+ Gets whether the computer is x64 (64 bits).
+}
+begin
+ Result := (not isForcedX86) and Is64BitInstallMode and (ProcessorArchitecture = paX64);
+end;
+
+function IsIA64: boolean;
+{
+ Gets whether the computer is IA64 (Itanium 64 bits).
+}
+begin
+ Result := (not isForcedX86) and Is64BitInstallMode and (ProcessorArchitecture = paIA64);
+end;
+
+function GetString(x86, x64, ia64: String): String;
+{
+ Gets a string depending on the computer architecture.
+ Parameters:
+ x86: the string if the computer is x86
+ x64: the string if the computer is x64
+ ia64: the string if the computer is IA64
+}
+begin
+ if IsX64() and (x64 <> '') then begin
+ Result := x64;
+ end else if IsIA64() and (ia64 <> '') then begin
+ Result := ia64;
+ end else begin
+ Result := x86;
+ end;
+end;
+
+function GetArchitectureString(): String;
+{
+ Gets the "standard" architecture suffix string.
+ Returns either _x64, _ia64 or nothing.
+}
+begin
+ if IsX64() then begin
+ Result := '_x64';
+ end else if IsIA64() then begin
+ Result := '_ia64';
+ end else begin
+ Result := '';
+ end;
+end;
+
+procedure SetForceX86(value: boolean);
+{
+ Forces the setup to use X86 products
+}
+begin
+ isForcedX86 := value;
+end;
diff --git a/dist/inno/scripts/products/msiproduct.iss b/dist/inno/scripts/products/msiproduct.iss
new file mode 100644
index 0000000..35a8d23
--- /dev/null
+++ b/dist/inno/scripts/products/msiproduct.iss
@@ -0,0 +1,49 @@
+[Code]
+#ifdef UNICODE
+ #define AW "W"
+#else
+ #define AW "A"
+#endif
+
+type
+ INSTALLSTATE = Longint;
+const
+ INSTALLSTATE_INVALIDARG = -2; // An invalid parameter was passed to the function.
+ INSTALLSTATE_UNKNOWN = -1; // The product is neither advertised or installed.
+ INSTALLSTATE_ADVERTISED = 1; // The product is advertised but not installed.
+ INSTALLSTATE_ABSENT = 2; // The product is installed for a different user.
+ INSTALLSTATE_DEFAULT = 5; // The product is installed for the current user.
+
+function MsiQueryProductState(szProduct: string): INSTALLSTATE;
+external 'MsiQueryProductState{#AW}@msi.dll stdcall';
+
+function MsiEnumRelatedProducts(szUpgradeCode: string; nReserved: dword; nIndex: dword; szProductCode: string): integer;
+external 'MsiEnumRelatedProducts{#AW}@msi.dll stdcall';
+
+function MsiGetProductInfo(szProductCode: string; szProperty: string; szValue: string; var nvalueSize: dword): integer;
+external 'MsiGetProductInfo{#AW}@msi.dll stdcall';
+
+function msiproduct(productID: string): boolean;
+begin
+ Result := MsiQueryProductState(productID) = INSTALLSTATE_DEFAULT;
+end;
+
+function msiproductupgrade(upgradeCode: string; minVersion: string): boolean;
+var
+ productCode, version: string;
+ valueSize: dword;
+begin
+ SetLength(productCode, 39);
+ Result := false;
+
+ if (MsiEnumRelatedProducts(upgradeCode, 0, 0, productCode) = 0) then begin
+ SetLength(version, 39);
+ valueSize := Length(version);
+
+ if (MsiGetProductInfo(productCode, 'VersionString', version, valueSize) = 0) then begin
+ Result := compareversion(version, minVersion) >= 0;
+ end;
+ end;
+end;
+
+[Setup]
diff --git a/dist/inno/scripts/products/stringversion.iss b/dist/inno/scripts/products/stringversion.iss
new file mode 100644
index 0000000..4cb114f
--- /dev/null
+++ b/dist/inno/scripts/products/stringversion.iss
@@ -0,0 +1,62 @@
+[Code]
+function stringtoversion(var temp: String): Integer;
+var
+ part: String;
+ pos1: Integer;
+
+begin
+ if (Length(temp) = 0) then begin
+ Result := -1;
+ Exit;
+ end;
+
+ pos1 := Pos('.', temp);
+ if (pos1 = 0) then begin
+ Result := StrToInt(temp);
+ temp := '';
+ end else begin
+ part := Copy(temp, 1, pos1 - 1);
+ temp := Copy(temp, pos1 + 1, Length(temp));
+ Result := StrToInt(part);
+ end;
+end;
+
+function compareinnerversion(var x, y: String): Integer;
+var
+ num1, num2: Integer;
+
+begin
+ num1 := stringtoversion(x);
+ num2 := stringtoversion(y);
+ if (num1 = -1) and (num2 = -1) then begin
+ Result := 0;
+ Exit;
+ end;
+
+ if (num1 < 0) then begin
+ num1 := 0;
+ end;
+ if (num2 < 0) then begin
+ num2 := 0;
+ end;
+
+ if (num1 < num2) then begin
+ Result := -1;
+ end else if (num1 > num2) then begin
+ Result := 1;
+ end else begin
+ Result := compareinnerversion(x, y);
+ end;
+end;
+
+function compareversion(versionA, versionB: String): Integer;
+var
+ temp1, temp2: String;
+
+begin
+ temp1 := versionA;
+ temp2 := versionB;
+ Result := compareinnerversion(temp1, temp2);
+end;
+
+[Setup]
diff --git a/dist/inno/scripts/products/vcredist2017.iss b/dist/inno/scripts/products/vcredist2017.iss
new file mode 100644
index 0000000..da20e03
--- /dev/null
+++ b/dist/inno/scripts/products/vcredist2017.iss
@@ -0,0 +1,32 @@
+; requires Windows 10, Windows 7 Service Pack 1, Windows 8, Windows 8.1, Windows Server 2003 Service Pack 2, Windows Server 2008 R2 SP1, Windows Server 2008 Service Pack 2, Windows Server 2012, Windows Vista Service Pack 2, Windows XP Service Pack 3
+; http://www.visualstudio.com/en-us/downloads/
+
+[CustomMessages]
+vcredist2017_title=Visual C++ 2017 Redistributable
+vcredist2017_title_x64=Visual C++ 2017 64-Bit Redistributable
+
+vcredist2017_size=13.7 MB
+vcredist2017_size_x64=14.5 MB
+
+[Code]
+const
+ vcredist2017_url = 'http://download.microsoft.com/download/1/f/e/1febbdb2-aded-4e14-9063-39fb17e88444/vc_redist.x86.exe';
+ vcredist2017_url_x64 = 'http://download.microsoft.com/download/3/b/f/3bf6e759-c555-4595-8973-86b7b4312927/vc_redist.x64.exe';
+
+ vcredist2017_upgradecode = '{65E5BD06-6392-3027-8C26-853107D3CF1A}';
+ vcredist2017_upgradecode_x64 = '{36F68A90-239C-34DF-B58C-64B30153CE35}';
+
+procedure vcredist2017(minVersion: string);
+begin
+ if (not IsIA64()) then begin
+ if (not msiproductupgrade(GetString(vcredist2017_upgradecode, vcredist2017_upgradecode_x64, ''), minVersion)) then
+ AddProduct('vcredist2017' + GetArchitectureString() + '.exe',
+ '/passive /norestart',
+ CustomMessage('vcredist2017_title' + GetArchitectureString()),
+ CustomMessage('vcredist2017_size' + GetArchitectureString()),
+ GetString(vcredist2017_url, vcredist2017_url_x64, ''),
+ false, false, false);
+ end;
+end;
+
+[Setup]
diff --git a/dist/inno/scripts/products/winversion.iss b/dist/inno/scripts/products/winversion.iss
new file mode 100644
index 0000000..e1aff98
--- /dev/null
+++ b/dist/inno/scripts/products/winversion.iss
@@ -0,0 +1,49 @@
+[Code]
+var
+ WindowsVersion: TWindowsVersion;
+
+procedure initwinversion();
+begin
+ GetWindowsVersionEx(WindowsVersion);
+end;
+
+function exactwinversion(MajorVersion, MinorVersion: integer): boolean;
+begin
+ Result := (WindowsVersion.Major = MajorVersion) and (WindowsVersion.Minor = MinorVersion);
+end;
+
+function minwinversion(MajorVersion, MinorVersion: integer): boolean;
+begin
+ Result := (WindowsVersion.Major > MajorVersion) or ((WindowsVersion.Major = MajorVersion) and (WindowsVersion.Minor >= MinorVersion));
+end;
+
+function maxwinversion(MajorVersion, MinorVersion: integer): boolean;
+begin
+ Result := (WindowsVersion.Major < MajorVersion) or ((WindowsVersion.Major = MajorVersion) and (WindowsVersion.Minor <= MinorVersion));
+end;
+
+function exactwinspversion(MajorVersion, MinorVersion, SpVersion: integer): boolean;
+begin
+ if exactwinversion(MajorVersion, MinorVersion) then
+ Result := WindowsVersion.ServicePackMajor = SpVersion
+ else
+ Result := true;
+end;
+
+function minwinspversion(MajorVersion, MinorVersion, SpVersion: integer): boolean;
+begin
+ if exactwinversion(MajorVersion, MinorVersion) then
+ Result := WindowsVersion.ServicePackMajor >= SpVersion
+ else
+ Result := true;
+end;
+
+function maxwinspversion(MajorVersion, MinorVersion, SpVersion: integer): boolean;
+begin
+ if exactwinversion(MajorVersion, MinorVersion) then
+ Result := WindowsVersion.ServicePackMajor <= SpVersion
+ else
+ Result := true;
+end;
+
+[Setup]
diff --git a/dist/macos/bundle/Barrier.app/Contents/Info.plist.in b/dist/macos/bundle/Barrier.app/Contents/Info.plist.in
new file mode 100644
index 0000000..b973f5e
--- /dev/null
+++ b/dist/macos/bundle/Barrier.app/Contents/Info.plist.in
@@ -0,0 +1,32 @@
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleDisplayName</key>
+ <string>Barrier</string>
+ <key>CFBundleExecutable</key>
+ <string>barrier.sh</string>
+ <key>CFBundleIconFile</key>
+ <string>Barrier.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>barrier</string>
+ <!-- TODO: Fix this in v2.0 //-->
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>Barrier</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>SYN1</string>
+ <key>CFBundleShortVersionString</key>
+ <string>@BARRIER_VERSION@</string>
+ <key>CFBundleVersion</key>
+ <string>@BARRIER_VERSION@</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>© 2018 Debauchee Open Source Group</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.9.0</string>
+ </dict>
+</plist>
diff --git a/dist/macos/bundle/Barrier.app/Contents/PkgInfo b/dist/macos/bundle/Barrier.app/Contents/PkgInfo
new file mode 100644
index 0000000..583e36a
--- /dev/null
+++ b/dist/macos/bundle/Barrier.app/Contents/PkgInfo
@@ -0,0 +1 @@
+APPLSYN1 \ No newline at end of file
diff --git a/dist/macos/bundle/Barrier.app/Contents/Resources/Barrier.icns b/dist/macos/bundle/Barrier.app/Contents/Resources/Barrier.icns
new file mode 100644
index 0000000..9f573da
--- /dev/null
+++ b/dist/macos/bundle/Barrier.app/Contents/Resources/Barrier.icns
Binary files differ
diff --git a/dist/macos/bundle/build_installer.sh.in b/dist/macos/bundle/build_installer.sh.in
new file mode 100755
index 0000000..f939b77
--- /dev/null
+++ b/dist/macos/bundle/build_installer.sh.in
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+# change this to rename the installer package
+B_DMG="Barrier-@BARRIER_VERSION@.dmg"
+
+# sanity check so we don't distribute packages full of debug symbols
+if [ "@CMAKE_BUILD_TYPE@" != "Release" ]; then
+ echo Will only build installers for Release builds
+ exit 1
+fi
+
+cd @CMAKE_CURRENT_SOURCE_DIR@/build/bundle || exit 1
+
+B_REREF_SCRIPT=@CMAKE_CURRENT_SOURCE_DIR@/build/bundle/reref_dylibs.sh
+if [ ! -x $B_REREF_SCRIPT ]; then
+ echo Missing script: $B_REREF_SCRIPT
+ exit 1
+fi
+
+# remove any old copies so there's no confusion about whether this
+# process completes successfully or not
+rm -rf temp.dmg $B_DMG
+
+cd Barrier.app/Contents 2>/dev/null
+if [ $? -ne 0 ]; then
+ echo Please make sure that the build completed successfully
+ echo before trying to create the installer.
+ exit 1
+fi
+
+# MacOS folder holds the executables, non-system libraries,
+# and the startup script
+rm -rf MacOS
+mkdir MacOS || exit 1
+cd MacOS || exit 1
+
+# copy all executables
+cp @CMAKE_RUNTIME_OUTPUT_DIRECTORY@/* . || exit 1
+
+# copy the qt platform plugin
+# TODO: this is hacky and will probably break if there is more than one qt
+# version installed. need a better way to find this library
+B_COCOA=$(find /usr/local/Cellar/qt -type f -name libqcocoa.dylib | head -1)
+if [ $? -ne 0 ] || [ "x$B_COCOA" = "x" ]; then
+ echo "Could not find cocoa platform plugin"
+ exit 1
+fi
+mkdir platforms
+cp $B_COCOA platforms/ || exit 1
+
+# make sure we can r/w all these binaries
+chmod -R u+rw * || exit 1
+
+# only one executable (barrier) needs non-system libraries although it's
+# libraries can call each other. use a recursive script to handle the
+# re-referencing
+$B_REREF_SCRIPT barrier || exit 1
+# the cocoa platform plugin also needs to know where to find the qt libraries.
+# because it exists in a subdirectory we append ../ to the relative path of the
+# libraries in its metadata
+$B_REREF_SCRIPT platforms/libqcocoa.dylib ../ || exit 1
+
+# create a startup script that will change to the binary directory
+# before starting barrier
+printf "%s\n" "#!/bin/sh" "cd \$(dirname \$0)" "exec ./barrier" > barrier.sh
+chmod +x barrier.sh
+
+# create the DMG to be distributed in build/bundle
+cd ../../..
+hdiutil create -size 64m -fs HFS+ -volname "Barrier" temp.dmg || exit 1
+hdiutil attach temp.dmg -mountpoint mnt || exit 1
+cp -r Barrier.app mnt/ || exit 1
+hdiutil detach mnt || exit 1
+hdiutil convert temp.dmg -format UDZO -o $B_DMG || exit 1
+rm temp.dmg
+
+echo "Installer created successfully"
diff --git a/dist/macos/bundle/reref_dylibs.sh b/dist/macos/bundle/reref_dylibs.sh
new file mode 100755
index 0000000..15191bd
--- /dev/null
+++ b/dist/macos/bundle/reref_dylibs.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# $1 = binary (program or dylib)
+B_TARGET=$1
+if [ "x$B_TARGET" = "x" ]; then
+ echo Which binary needs to be re-referenced?
+ exit 1
+fi
+
+cd $(dirname $B_TARGET) || exit 1
+
+# where to find non-system libraries relative to target's directory.
+# the vast majority of the time this should be empty
+B_REL_PATH=$2
+
+# we're in target's directory now. trim off the path
+B_TARGET=$(basename $B_TARGET)
+
+# get a list of non-system libraries and make local copies
+B_LIBS=$(otool -XL $B_TARGET | awk '{ print $1 }' | grep -Ev '^(/usr/lib|/System)')
+[ $? -ne 0 ] && exit 1
+for B_LIB in $B_LIBS; do
+ B_LIB_NAME=$(basename $B_LIB)
+
+ # ignore self-references
+ [ "$B_TARGET" = "$B_LIB_NAME" ] && continue
+
+ B_DST=${B_REL_PATH}${B_LIB_NAME}
+ if [ ! -e $B_DST ]; then
+ cp $B_LIB $B_DST || exit 1
+ chmod u+rw $B_DST || exit 1
+ # recursively call this script on libraries purposefully not passing
+ # $B_REL_PATH so that it is only used explicitly
+ $0 $B_DST
+ fi
+
+ # adjust the target's metadata to point to the local copy
+ # rather than the system-wide copy which would only exist on
+ # a development machine
+ install_name_tool -change $B_LIB @loader_path/$B_DST $B_TARGET || exit 1
+done
diff --git a/dist/rpm/barrier.spec.in b/dist/rpm/barrier.spec.in
new file mode 100644
index 0000000..b175ded
--- /dev/null
+++ b/dist/rpm/barrier.spec.in
@@ -0,0 +1,27 @@
+Name: barrier
+Version: @BARRIER_VERSION@
+Summary: Keyboard and mouse sharing solution
+Group: Applications/Productivity
+URL: https://github.com/debauchee/barrier/
+Source: https://github.com/debauchee/barrier/
+Vendor: Debauchee
+Packager: Debauchee <todo@mail.com>
+License: GPLv2
+Release: @BARRIER_BUILD_NUMBER@@BARRIER_SNAPSHOT_INFO@%{?dist}
+
+%description
+Barrier allows you to share one mouse and keyboard between multiple computers.
+Work seamlessly across Windows, macOS and Linux.
+
+%files
+%defattr(755,root,root,-)
+%{_bindir}/barrier
+%{_bindir}/barrierc
+%{_bindir}/barriers
+%{_bindir}/syntool
+%attr(644,-,-) %{_datarootdir}/applications/barrier.desktop
+%attr(644,-,-) %{_datarootdir}/icons/hicolor/scalable/apps/barrier.svg
+
+%changelog
+* Sat Jan 27 2018 Debauchee <todo@mail.com>
+- Initial version of the package
diff --git a/dist/wix/Barrier.sln b/dist/wix/Barrier.sln
new file mode 100644
index 0000000..c163c62
--- /dev/null
+++ b/dist/wix/Barrier.sln
@@ -0,0 +1,27 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.23107.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "Barrier", "Barrier.wixproj", "{D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Debug|x64 = Debug|x64
+ Release|x86 = Release|x86
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}.Debug|x86.ActiveCfg = Debug|x86
+ {D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}.Debug|x86.Build.0 = Debug|x86
+ {D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}.Debug|x64.ActiveCfg = Debug|x64
+ {D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}.Debug|x64.Build.0 = Debug|x64
+ {D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}.Release|x86.ActiveCfg = Release|x86
+ {D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}.Release|x86.Build.0 = Release|x86
+ {D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}.Release|x64.ActiveCfg = Release|x64
+ {D4BA9F39-6A35-4C8F-9CB2-67FCBE5CAB17}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/dist/wix/Barrier.wixproj b/dist/wix/Barrier.wixproj
new file mode 100644
index 0000000..ffc7701
--- /dev/null
+++ b/dist/wix/Barrier.wixproj
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <ProductVersion>3.10</ProductVersion>
+ <ProjectGuid>{d4ba9f39-6a35-4c8f-9cb2-67fcbe5cab17}</ProjectGuid>
+ <SchemaVersion>2.0</SchemaVersion>
+ <OutputName>Barrier</OutputName>
+ <OutputType>Package</OutputType>
+ <WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
+ <WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
+ <OutputPath>bin\$(Configuration)\</OutputPath>
+ <IntermediateOutputPath>wix\obj\$(Configuration)\</IntermediateOutputPath>
+ </PropertyGroup>
+ <ItemGroup>
+ <WixExtension Include="WixFirewallExtension">
+ <HintPath>$(WixExtDir)\WixFirewallExtension.dll</HintPath>
+ <Name>WixFirewallExtension</Name>
+ </WixExtension>
+ <WixExtension Include="WixUtilExtension">
+ <HintPath>$(WixExtDir)\WixUtilExtension.dll</HintPath>
+ <Name>WixUtilExtension</Name>
+ </WixExtension>
+ <WixExtension Include="WixUIExtension">
+ <HintPath>$(WixExtDir)\WixUIExtension.dll</HintPath>
+ <Name>WixUIExtension</Name>
+ </WixExtension>
+ <Compile Include="Product.wxs"/>
+ <Content Include="Include.wxi"/>
+ </ItemGroup>
+ <Import Project="$(WixTargetsPath)"/>
+</Project> \ No newline at end of file
diff --git a/dist/wix/Include.wxi.in b/dist/wix/Include.wxi.in
new file mode 100644
index 0000000..652d203
--- /dev/null
+++ b/dist/wix/Include.wxi.in
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Include>
+ <?define Name="Barrier" ?>
+ <?define Version="@BARRIER_VERSION@" ?>
+ <?define QtVersion="@QT_VERSION@" ?>
+ <?define Author="Debauchee Open Source Group" ?>
+ <?define BinPath="@CMAKE_RUNTIME_OUTPUT_DIRECTORY@/$(var.Configuration)" ?>
+ <?define ResPath="@CMAKE_CURRENT_SOURCE_DIR@/res" ?>
+ <?define ExtPath="@CMAKE_CURRENT_SOURCE_DIR@/ext" ?>
+ <?if $(var.Platform) = "x64" ?>
+ <?define ProgramFilesFolder="ProgramFiles64Folder" ?>
+ <?define PlatformSimpleName="64-bit" ?>
+ <?define UpgradeGuid="E8A4FA54-14B9-4FD1-8E00-7BC46555FDA0" ?>
+ <?define QtPath="E:\Qt\$(var.QtVersion)\msvc2015_64" ?>
+ <?else ?>
+ <?define ProgramFilesFolder="ProgramFilesFolder" ?>
+ <?define PlatformSimpleName="32-bit" ?>
+ <?define UpgradeGuid="BE0B9FD8-45E2-4A8E-A0D8-1F774D074A78" ?>
+ <?define QtPath="E:\Qt\$(var.QtVersion)\msvc2015" ?>
+ <?endif ?>
+ <?define QtBinPath="$(var.QtPath)\bin" ?>
+ <?define QtPlatformPath="$(var.QtPath)\plugins\platforms" ?>
+ <?define OpenSSLPath="$(var.ExtPath)\openssl\windows\$(var.Platform)" ?>
+ <?define OpenSSLBinPath="$(var.OpenSSLPath)\bin" ?>
+</Include>
diff --git a/dist/wix/Product.wxs b/dist/wix/Product.wxs
new file mode 100644
index 0000000..5cb924e
--- /dev/null
+++ b/dist/wix/Product.wxs
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:fire="http://schemas.microsoft.com/wix/FirewallExtension" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
+ <?include Include.wxi?>
+ <Product Id="*" Language="1033" Manufacturer="$(var.Author)" Name="$(var.Name) ($(var.PlatformSimpleName))" UpgradeCode="$(var.UpgradeGuid)" Version="$(var.Version)">
+ <Package Compressed="yes" InstallScope="perMachine" InstallerVersion="301"/>
+ <MajorUpgrade DowngradeErrorMessage="A newer version of $(var.Name) is already installed."/>
+ <MediaTemplate EmbedCab="yes"/>
+ <UIRef Id="WixUI_InstallDir"/>
+ <!-- causes ICE61 warning, but stops user from installing many instances from nightly builds. -->
+ <Upgrade Id="$(var.UpgradeGuid)">
+ <UpgradeVersion Minimum="0.0.0.0" Property="UPGRADE"/>
+ </Upgrade>
+ <Feature Id="ProductFeature" Title="$(var.Name)">
+ <ComponentGroupRef Id="ProductComponents"/>
+ <ComponentGroupRef Id="ProductQtPluginComponents"/>
+ <ComponentRef Id="RegistryEntries"/>
+ </Feature>
+ <DirectoryRef Id="TARGETDIR">
+ <Component Guid="7CF3564D-1F8E-4D3D-9781-E1EE22D5BD67" Id="RegistryEntries">
+ <RegistryKey ForceCreateOnInstall="yes" ForceDeleteOnUninstall="yes" Key="Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers" Root="HKLM">
+ <RegistryValue Name="[INSTALLFOLDER]barriers.exe" Type="string" Value="~ HIGHDPIAWARE WIN7RTM"/>
+ </RegistryKey>
+ <!-- Windows 8 and later only -->
+ <Condition><![CDATA[Installed OR (VersionNT >= 602)]]></Condition>
+ </Component>
+ </DirectoryRef>
+ <Icon Id="barrier.ico" SourceFile="$(var.ResPath)/barrier.ico"/>
+ <WixVariable Id="WixUILicenseRtf" Value="$(var.ResPath)\License.rtf"/>
+ <WixVariable Id="WixUIBannerBmp" Value="$(var.ResPath)\banner.bmp"/>
+ <WixVariable Id="WixUIDialogBmp" Value="$(var.ResPath)\dialog.bmp"/>
+ <Property Id="ARPPRODUCTICON" Value="barrier.ico"/>
+ <Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER"/>
+ <Property Id="MSIRESTARTMANAGERCONTROL" Value="Disable"/>
+ </Condition>
+ <CustomAction ExeCommand="" FileKey="GuiProgram" Id="StartGui" Return="asyncNoWait"/>
+ <UI>
+ <Publish Control="Finish" Dialog="ExitDialog" Event="DoAction" Value="StartGui">NOT Installed</Publish>
+ </UI>
+ </Product>
+ <Fragment>
+ <Directory Id="TARGETDIR" Name="SourceDir">
+ <Directory Id="$(var.ProgramFilesFolder)">
+ <Directory Id="INSTALLFOLDER" Name="$(var.Name)">
+ <Directory Id="OpenSSLDir" Name="OpenSSL"/>
+ <Directory Id="PlatformsDir" Name="Platforms"/>
+ </Directory>
+ </Directory>
+ <Directory Id="ProgramMenuFolder"/>
+ </Directory>
+ </Fragment>
+ <Fragment>
+ <ComponentGroup Directory="INSTALLFOLDER" Id="ProductComponents">
+ <Component Guid="EC9AD3B0-277C-4157-B5C8-5FD5B6A5F4AD" Id="Core">
+ <File KeyPath="yes" Source="$(var.BinPath)/barrierd.exe"/>
+ <ServiceInstall Description="Controls the $(var.Name) foreground processes." DisplayName="$(var.Name)" ErrorControl="normal" Id="ServiceInstall" Name="Barrier" Start="auto" Type="ownProcess">
+ <util:ServiceConfig FirstFailureActionType="restart" ResetPeriodInDays="1" RestartServiceDelayInSeconds="1" SecondFailureActionType="restart" ThirdFailureActionType="restart"/>
+ </ServiceInstall>
+ <ServiceControl Id="ServiceControl" Name="Barrier" Remove="uninstall" Start="install" Stop="both"/>
+ <File Source="$(var.BinPath)/barriers.exe">
+ <fire:FirewallException Id="ServerFirewallException" IgnoreFailure="yes" Name="$(var.Name)" Scope="any"/>
+ </File>
+ <File Source="$(var.BinPath)/barrierc.exe"/>
+ <File Source="$(var.BinPath)/syntool.exe"/>
+ <File Source="$(var.BinPath)/synwinhk.dll"/>
+ <File Source="$(var.OpenSSLBinPath)/libeay32.dll"/>
+ <File Source="$(var.OpenSSLBinPath)/ssleay32.dll"/>
+ <File Source="$(var.OpenSSLBinPath)/openssl.exe"/>
+ <File Source="$(var.ResPath)/openssl/barrier.conf"/>
+ </Component>
+ <Component Guid="BAC8149B-6287-45BF-9C27-43D71ED40214" Id="Gui">
+ <File Id="GuiProgram" KeyPath="yes" Source="$(var.BinPath)/barrier.exe">
+ <Shortcut Advertise="yes" Directory="ProgramMenuFolder" Icon="barrier.ico" Id="GuiShortcut" Name="$(var.Name)"/>
+ <fire:FirewallException Id="GuiFirewallException" IgnoreFailure="yes" Name="$(var.Name)" Scope="any"/>
+ </File>
+ <?if $(var.Configuration) = "Debug" ?>
+ <File Source="$(var.QtBinPath)\Qt5Cored.dll"/>
+ <File Source="$(var.QtBinPath)\Qt5Guid.dll"/>
+ <File Source="$(var.QtBinPath)\Qt5Networkd.dll"/>
+ <File Source="$(var.QtBinPath)\Qt5Svgd.dll"/>
+ <File Source="$(var.QtBinPath)\Qt5Widgetsd.dll"/>
+ <File Source="$(var.QtBinPath)\libGLESv2d.dll"/>
+ <File Source="$(var.QtBinPath)\libEGLd.dll"/>
+ <!-- Hacky -->
+ <File Source="C:\Program Files (x86)\Windows Kits\10\bin\$(var.Platform)\ucrt\ucrtbased.dll"/>
+ <?else ?>
+ <File Source="$(var.QtBinPath)\Qt5Core.dll"/>
+ <File Source="$(var.QtBinPath)\Qt5Gui.dll"/>
+ <File Source="$(var.QtBinPath)\Qt5Network.dll"/>
+ <File Source="$(var.QtBinPath)\Qt5Svg.dll"/>
+ <File Source="$(var.QtBinPath)\Qt5Widgets.dll"/>
+ <File Source="$(var.QtBinPath)\libGLESv2.dll"/>
+ <File Source="$(var.QtBinPath)\libEGL.dll"/>
+ <?endif ?>
+ </Component>
+ </ComponentGroup>
+ <ComponentGroup Directory="PlatformsDir" Id="ProductQtPluginComponents">
+ <Component Guid="684EFA14-856B-440E-A5E6-E90E04E36B41" Id="QtPlatformPlugin">
+ <?if $(var.Configuration) = "Debug" ?>
+ <File Source="$(var.QtPlatformPath)\qwindowsd.dll"/>
+ <?else ?>
+ <File Source="$(var.QtPlatformPath)\qwindows.dll"/>
+ <?endif ?>
+ </Component>
+ </ComponentGroup>
+ </Fragment>
+</Wix>
diff --git a/doc/MacReadme.txt b/doc/MacReadme.txt
new file mode 100644
index 0000000..9e194e1
--- /dev/null
+++ b/doc/MacReadme.txt
@@ -0,0 +1,15 @@
+Mac OS X Readme
+===============
+
+To install on Mac OS X with the .zip distribution (first seen in 1.3.6) you must follow these steps:
+
+ 1. Extract the zip file to any location (usually double click will do this)
+ 2. Open Terminal, and cd to the extracted directory (e.g. /Users/my-name/Downloads/extracted-dir/)
+ 3. Copy the binaries to /usr/bin using: sudo cp barrier* /usr/bin
+ 4. Correct the permissions and ownership: sudo chown root:wheel /usr/bin/barrier*; sudo chmod 555 /usr/bin/barrier*
+
+Alternatively, you can copy the binaries as root. How to enable the root user in Mac OS X:
+ http://support.apple.com/en-us/ht1528
+
+Once the binaries have been copied to /usr/bin, you should follow the configuration guide:
+ http://synergy2.sourceforge.net/configuration.html
diff --git a/doc/QtCodeStyle.xml b/doc/QtCodeStyle.xml
new file mode 100644
index 0000000..e621c4f
--- /dev/null
+++ b/doc/QtCodeStyle.xml
@@ -0,0 +1,234 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorCodeStyle>
+<!-- Written by QtCreator 3.0.1, 2014-02-14T09:50:24. -->
+<qtcreator>
+ <data>
+ <variable>CodeStyleData</variable>
+ <valuemap type="QVariantMap">
+ <value type="bool" key="AlignAssignments">false</value>
+ <value type="bool" key="AutoSpacesForTabs">false</value>
+ <value type="bool" key="BindStarToIdentifier">true</value>
+ <value type="bool" key="BindStarToLeftSpecifier">false</value>
+ <value type="bool" key="BindStarToRightSpecifier">false</value>
+ <value type="bool" key="BindStarToTypeName">false</value>
+ <value type="bool" key="ExtraPaddingForConditionsIfConfusingAlign">true</value>
+ <value type="bool" key="IndentAccessSpecifiers">false</value>
+ <value type="bool" key="IndentBlockBody">true</value>
+ <value type="bool" key="IndentBlockBraces">false</value>
+ <value type="bool" key="IndentBlocksRelativeToSwitchLabels">false</value>
+ <value type="bool" key="IndentClassBraces">false</value>
+ <value type="bool" key="IndentControlFlowRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentDeclarationsRelativeToAccessSpecifiers">true</value>
+ <value type="bool" key="IndentEnumBraces">false</value>
+ <value type="bool" key="IndentFunctionBody">true</value>
+ <value type="bool" key="IndentFunctionBraces">false</value>
+ <value type="bool" key="IndentNamespaceBody">false</value>
+ <value type="bool" key="IndentNamespaceBraces">false</value>
+ <value type="int" key="IndentSize">4</value>
+ <value type="bool" key="IndentStatementsRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentSwitchLabels">false</value>
+ <value type="int" key="PaddingMode">2</value>
+ <value type="bool" key="SpacesForTabs">false</value>
+ <value type="int" key="TabSize">4</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>DisplayName</variable>
+ <value type="QString">Barrier</value>
+ </data>
+</qtcreator>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorCodeStyle>
+<!-- Written by QtCreator 3.0.1, 2014-02-14T09:50:24. -->
+<qtcreator>
+ <data>
+ <variable>CodeStyleData</variable>
+ <valuemap type="QVariantMap">
+ <value type="bool" key="AlignAssignments">false</value>
+ <value type="bool" key="AutoSpacesForTabs">false</value>
+ <value type="bool" key="BindStarToIdentifier">true</value>
+ <value type="bool" key="BindStarToLeftSpecifier">false</value>
+ <value type="bool" key="BindStarToRightSpecifier">false</value>
+ <value type="bool" key="BindStarToTypeName">false</value>
+ <value type="bool" key="ExtraPaddingForConditionsIfConfusingAlign">true</value>
+ <value type="bool" key="IndentAccessSpecifiers">false</value>
+ <value type="bool" key="IndentBlockBody">true</value>
+ <value type="bool" key="IndentBlockBraces">false</value>
+ <value type="bool" key="IndentBlocksRelativeToSwitchLabels">false</value>
+ <value type="bool" key="IndentClassBraces">false</value>
+ <value type="bool" key="IndentControlFlowRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentDeclarationsRelativeToAccessSpecifiers">true</value>
+ <value type="bool" key="IndentEnumBraces">false</value>
+ <value type="bool" key="IndentFunctionBody">true</value>
+ <value type="bool" key="IndentFunctionBraces">false</value>
+ <value type="bool" key="IndentNamespaceBody">false</value>
+ <value type="bool" key="IndentNamespaceBraces">false</value>
+ <value type="int" key="IndentSize">4</value>
+ <value type="bool" key="IndentStatementsRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentSwitchLabels">false</value>
+ <value type="int" key="PaddingMode">2</value>
+ <value type="bool" key="SpacesForTabs">false</value>
+ <value type="int" key="TabSize">4</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>DisplayName</variable>
+ <value type="QString">Barrier</value>
+ </data>
+</qtcreator>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorCodeStyle>
+<!-- Written by QtCreator 3.0.1, 2014-02-14T09:50:24. -->
+<qtcreator>
+ <data>
+ <variable>CodeStyleData</variable>
+ <valuemap type="QVariantMap">
+ <value type="bool" key="AlignAssignments">false</value>
+ <value type="bool" key="AutoSpacesForTabs">false</value>
+ <value type="bool" key="BindStarToIdentifier">true</value>
+ <value type="bool" key="BindStarToLeftSpecifier">false</value>
+ <value type="bool" key="BindStarToRightSpecifier">false</value>
+ <value type="bool" key="BindStarToTypeName">false</value>
+ <value type="bool" key="ExtraPaddingForConditionsIfConfusingAlign">true</value>
+ <value type="bool" key="IndentAccessSpecifiers">false</value>
+ <value type="bool" key="IndentBlockBody">true</value>
+ <value type="bool" key="IndentBlockBraces">false</value>
+ <value type="bool" key="IndentBlocksRelativeToSwitchLabels">false</value>
+ <value type="bool" key="IndentClassBraces">false</value>
+ <value type="bool" key="IndentControlFlowRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentDeclarationsRelativeToAccessSpecifiers">true</value>
+ <value type="bool" key="IndentEnumBraces">false</value>
+ <value type="bool" key="IndentFunctionBody">true</value>
+ <value type="bool" key="IndentFunctionBraces">false</value>
+ <value type="bool" key="IndentNamespaceBody">false</value>
+ <value type="bool" key="IndentNamespaceBraces">false</value>
+ <value type="int" key="IndentSize">4</value>
+ <value type="bool" key="IndentStatementsRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentSwitchLabels">false</value>
+ <value type="int" key="PaddingMode">2</value>
+ <value type="bool" key="SpacesForTabs">false</value>
+ <value type="int" key="TabSize">4</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>DisplayName</variable>
+ <value type="QString">Barrier</value>
+ </data>
+</qtcreator>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorCodeStyle>
+<!-- Written by QtCreator 3.0.1, 2014-02-14T09:50:24. -->
+<qtcreator>
+ <data>
+ <variable>CodeStyleData</variable>
+ <valuemap type="QVariantMap">
+ <value type="bool" key="AlignAssignments">false</value>
+ <value type="bool" key="AutoSpacesForTabs">false</value>
+ <value type="bool" key="BindStarToIdentifier">true</value>
+ <value type="bool" key="BindStarToLeftSpecifier">false</value>
+ <value type="bool" key="BindStarToRightSpecifier">false</value>
+ <value type="bool" key="BindStarToTypeName">false</value>
+ <value type="bool" key="ExtraPaddingForConditionsIfConfusingAlign">true</value>
+ <value type="bool" key="IndentAccessSpecifiers">false</value>
+ <value type="bool" key="IndentBlockBody">true</value>
+ <value type="bool" key="IndentBlockBraces">false</value>
+ <value type="bool" key="IndentBlocksRelativeToSwitchLabels">false</value>
+ <value type="bool" key="IndentClassBraces">false</value>
+ <value type="bool" key="IndentControlFlowRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentDeclarationsRelativeToAccessSpecifiers">true</value>
+ <value type="bool" key="IndentEnumBraces">false</value>
+ <value type="bool" key="IndentFunctionBody">true</value>
+ <value type="bool" key="IndentFunctionBraces">false</value>
+ <value type="bool" key="IndentNamespaceBody">false</value>
+ <value type="bool" key="IndentNamespaceBraces">false</value>
+ <value type="int" key="IndentSize">4</value>
+ <value type="bool" key="IndentStatementsRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentSwitchLabels">false</value>
+ <value type="int" key="PaddingMode">2</value>
+ <value type="bool" key="SpacesForTabs">false</value>
+ <value type="int" key="TabSize">4</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>DisplayName</variable>
+ <value type="QString">Barrier</value>
+ </data>
+</qtcreator>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorCodeStyle>
+<!-- Written by QtCreator 3.0.1, 2014-02-14T09:50:24. -->
+<qtcreator>
+ <data>
+ <variable>CodeStyleData</variable>
+ <valuemap type="QVariantMap">
+ <value type="bool" key="AlignAssignments">false</value>
+ <value type="bool" key="AutoSpacesForTabs">false</value>
+ <value type="bool" key="BindStarToIdentifier">true</value>
+ <value type="bool" key="BindStarToLeftSpecifier">false</value>
+ <value type="bool" key="BindStarToRightSpecifier">false</value>
+ <value type="bool" key="BindStarToTypeName">false</value>
+ <value type="bool" key="ExtraPaddingForConditionsIfConfusingAlign">true</value>
+ <value type="bool" key="IndentAccessSpecifiers">false</value>
+ <value type="bool" key="IndentBlockBody">true</value>
+ <value type="bool" key="IndentBlockBraces">false</value>
+ <value type="bool" key="IndentBlocksRelativeToSwitchLabels">false</value>
+ <value type="bool" key="IndentClassBraces">false</value>
+ <value type="bool" key="IndentControlFlowRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentDeclarationsRelativeToAccessSpecifiers">true</value>
+ <value type="bool" key="IndentEnumBraces">false</value>
+ <value type="bool" key="IndentFunctionBody">true</value>
+ <value type="bool" key="IndentFunctionBraces">false</value>
+ <value type="bool" key="IndentNamespaceBody">false</value>
+ <value type="bool" key="IndentNamespaceBraces">false</value>
+ <value type="int" key="IndentSize">4</value>
+ <value type="bool" key="IndentStatementsRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentSwitchLabels">false</value>
+ <value type="int" key="PaddingMode">2</value>
+ <value type="bool" key="SpacesForTabs">false</value>
+ <value type="int" key="TabSize">4</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>DisplayName</variable>
+ <value type="QString">Barrier</value>
+ </data>
+</qtcreator>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE QtCreatorCodeStyle>
+<!-- Written by QtCreator 3.0.1, 2014-02-14T09:50:24. -->
+<qtcreator>
+ <data>
+ <variable>CodeStyleData</variable>
+ <valuemap type="QVariantMap">
+ <value type="bool" key="AlignAssignments">false</value>
+ <value type="bool" key="AutoSpacesForTabs">false</value>
+ <value type="bool" key="BindStarToIdentifier">true</value>
+ <value type="bool" key="BindStarToLeftSpecifier">false</value>
+ <value type="bool" key="BindStarToRightSpecifier">false</value>
+ <value type="bool" key="BindStarToTypeName">false</value>
+ <value type="bool" key="ExtraPaddingForConditionsIfConfusingAlign">true</value>
+ <value type="bool" key="IndentAccessSpecifiers">false</value>
+ <value type="bool" key="IndentBlockBody">true</value>
+ <value type="bool" key="IndentBlockBraces">false</value>
+ <value type="bool" key="IndentBlocksRelativeToSwitchLabels">false</value>
+ <value type="bool" key="IndentClassBraces">false</value>
+ <value type="bool" key="IndentControlFlowRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentDeclarationsRelativeToAccessSpecifiers">true</value>
+ <value type="bool" key="IndentEnumBraces">false</value>
+ <value type="bool" key="IndentFunctionBody">true</value>
+ <value type="bool" key="IndentFunctionBraces">false</value>
+ <value type="bool" key="IndentNamespaceBody">false</value>
+ <value type="bool" key="IndentNamespaceBraces">false</value>
+ <value type="int" key="IndentSize">4</value>
+ <value type="bool" key="IndentStatementsRelativeToSwitchLabels">true</value>
+ <value type="bool" key="IndentSwitchLabels">false</value>
+ <value type="int" key="PaddingMode">2</value>
+ <value type="bool" key="SpacesForTabs">false</value>
+ <value type="int" key="TabSize">4</value>
+ </valuemap>
+ </data>
+ <data>
+ <variable>DisplayName</variable>
+ <value type="QString">Barrier</value>
+ </data>
+</qtcreator>
diff --git a/doc/UpdateManpages.txt b/doc/UpdateManpages.txt
new file mode 100644
index 0000000..cdbe86f
--- /dev/null
+++ b/doc/UpdateManpages.txt
@@ -0,0 +1,5 @@
+To recreate the barrierc manpage use a command like:
+ help2man -N --output=barrierc.1 --name='Barrier Keyboard/Mouse Client' /path/to/barrierc
+
+And for barriers:
+ help2man -N --output=barriers.1 --name='Barrier Keyboard/Mouse Server' /path/to/barriers
diff --git a/doc/barrier.conf.example b/doc/barrier.conf.example
new file mode 100644
index 0000000..5815875
--- /dev/null
+++ b/doc/barrier.conf.example
@@ -0,0 +1,37 @@
+# sample barrier configuration file
+#
+# comments begin with the # character and continue to the end of
+# line. comments may appear anywhere the syntax permits.
+
+section: screens
+ # three hosts named: moe, larry, and curly
+ moe:
+ larry:
+ curly:
+end
+
+section: links
+ # larry is to the right of moe and curly is above moe
+ moe:
+ right = larry
+ up = curly
+
+ # moe is to the left of larry and curly is above larry.
+ # note that curly is above both moe and larry and moe
+ # and larry have a symmetric connection (they're in
+ # opposite directions of each other).
+ larry:
+ left = moe
+ up = curly
+
+ # larry is below curly. if you move up from moe and then
+ # down, you'll end up on larry.
+ curly:
+ down = larry
+end
+
+section: aliases
+ # curly is also known as shemp
+ curly:
+ shemp
+end
diff --git a/doc/barrier.conf.example-advanced b/doc/barrier.conf.example-advanced
new file mode 100644
index 0000000..ad9df29
--- /dev/null
+++ b/doc/barrier.conf.example-advanced
@@ -0,0 +1,55 @@
+# sample barrier configuration file
+#
+# comments begin with the # character and continue to the end of
+# line. comments may appear anywhere the syntax permits.
+
+# This example uses 3 computers. A laptop and two desktops (one a mac)
+# They are arranged in the following configuration with Desktop1 acting as the server
+# Desktop 2 has 3 screens arranged around desktop1
+#
+# +--------+ +---------+
+# |Desktop2| |Desktop2 |
+# | | | |
+# +--------+ +---------+
+# +-------+ +--------+ +---------+
+# |Laptop | |Desktop1| |Desktop2 |
+# | | | | | |
+# +-------+ +--------+ +---------+
+#
+# The laptop comes and goes but that doesn't really affect this configuration
+
+# The screens section is for the logical or short name of the computers
+section: screens
+ # three computers that are logically named: desktop1, desktop2, and laptop
+ desktop1:
+ desktop2:
+ laptop:
+end
+
+section: links
+ # larry is to the right of moe and curly is above moe
+ moe:
+ right = larry
+ up = curly
+
+ # moe is to the left of larry and curly is above larry.
+ # note that curly is above both moe and larry and moe
+ # and larry have a symmetric connection (they're in
+ # opposite directions of each other).
+ larry:
+ left = moe
+ up = curly
+
+ # larry is below curly. if you move up from moe and then
+ # down, you'll end up on larry.
+ curly:
+ down = larry
+end
+
+# The aliases section is to map the full names of the computers to their logical names used in the screens section
+# One way to find the actual name of a comptuer is to run hostname from a command window
+section: aliases
+ # Laptop is actually known as John-Smiths-MacBook-3.local
+ desktop2:
+ John-Smiths-MacBook-3.local
+end
diff --git a/doc/barrier.conf.example-basic b/doc/barrier.conf.example-basic
new file mode 100644
index 0000000..39ff6d6
--- /dev/null
+++ b/doc/barrier.conf.example-basic
@@ -0,0 +1,39 @@
+# sample barrier configuration file
+#
+# comments begin with the # character and continue to the end of
+# line. comments may appear anywhere the syntax permits.
+# +-------+ +--------+ +---------+
+# |Laptop | |Desktop1| |iMac |
+# | | | | | |
+# +-------+ +--------+ +---------+
+
+section: screens
+ # three hosts named: Laptop, Desktop1, and iMac
+ # These are the nice names of the hosts to make it easy to write the config file
+ # The aliases section below contain the "actual" names of the hosts (their hostnames)
+ Laptop:
+ Desktop1:
+ iMac:
+end
+
+section: links
+ # iMac is to the right of Desktop1
+ # Laptop is to the left of Desktop1
+ Desktop1:
+ right = iMac
+ left = Laptop
+
+ # Desktop1 is to the right of Laptop
+ Laptop:
+ right = Desktop1
+
+ # Desktop1 is to the left of iMac
+ iMac:
+ left = Desktop1
+end
+
+section: aliases
+ # The "real" name of iMac is John-Smiths-iMac-3.local. If we wanted we could remove this alias and instead use John-Smiths-iMac-3.local everywhere iMac is above. Hopefully it should be easy to see why using an alias is nicer
+ iMac:
+ John-Smiths-iMac-3.local
+end
diff --git a/doc/barrierc.1 b/doc/barrierc.1
new file mode 100644
index 0000000..404df90
--- /dev/null
+++ b/doc/barrierc.1
@@ -0,0 +1,72 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
+.TH BARRIERC "1" "March 2018" "barrierc 2.0.0, protocol version 1.6" "User Commands"
+.SH NAME
+barrierc \- Barrier Keyboard/Mouse Client
+.SH SYNOPSIS
+.B barrierc
+[\fI\,--yscroll <delta>\/\fR] [\fI\,--display <display>\/\fR] [\fI\,--no-xinitthreads\/\fR] [\fI\,--daemon|--no-daemon\/\fR] [\fI\,--name <screen-name>\/\fR] [\fI\,--restart|--no-restart\/\fR] [\fI\,--debug <level>\/\fR] \fI\,<server-address>\/\fR
+.SH DESCRIPTION
+Start the barrier client and connect to a remote server component.
+.SH OPTIONS
+.TP
+\fB\-d\fR, \fB\-\-debug\fR <level>
+filter out log messages with priority below level.
+level may be: FATAL, ERROR, WARNING, NOTE, INFO,
+DEBUG, DEBUG1, DEBUG2.
+.TP
+\fB\-n\fR, \fB\-\-name\fR <screen\-name> use screen\-name instead the hostname to identify
+this screen in the configuration.
+.TP
+\fB\-1\fR, \fB\-\-no\-restart\fR
+do not try to restart on failure.
+.TP
+\fB\-\-restart\fR
+restart the server automatically if it fails. (*)
+.TP
+\fB\-l\fR \fB\-\-log\fR <file>
+write log messages to file.
+.TP
+\fB\-\-no\-tray\fR
+disable the system tray icon.
+.TP
+\fB\-\-enable\-drag\-drop\fR
+enable file drag & drop.
+.TP
+\fB\-\-enable\-crypto\fR
+enable the crypto (ssl) plugin.
+.TP
+\fB\-\-display\fR <display>
+connect to the X server at <display>
+.TP
+\fB\-\-no\-xinitthreads\fR
+do not call XInitThreads()
+.TP
+\fB\-f\fR, \fB\-\-no\-daemon\fR
+run in the foreground.
+.TP
+\fB\-\-daemon\fR
+run as a daemon. (*)
+.TP
+\fB\-\-yscroll\fR <delta>
+defines the vertical scrolling delta, which is
+120 by default.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+display this help and exit.
+.TP
+\fB\-\-version\fR
+display version information and exit.
+.PP
+Default options are marked with a *
+.PP
+The server address is of the form: [<hostname>][:<port>]. The hostname
+must be the address or hostname of the server. The port overrides the
+default port, 24800.
+.SH COPYRIGHT
+Copyright \(co 2018 Debauchee Open Source Group
+.br
+Copyright \(co 2012\-2016 Symless Ltd.
+.br
+Copyright \(co 2008\-2014 Nick Bolton
+.br
+Copyright \(co 2002\-2014 Chris Schoeneman
diff --git a/doc/barriers.1 b/doc/barriers.1
new file mode 100644
index 0000000..655d417
--- /dev/null
+++ b/doc/barriers.1
@@ -0,0 +1,81 @@
+.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.6.
+.TH BARRIERS "1" "March 2018" "barriers 2.0.0, protocol version 1.6" "User Commands"
+.SH NAME
+barriers \- Barrier Keyboard/Mouse Server
+.SH SYNOPSIS
+.B barriers
+[\fI\,--address <address>\/\fR] [\fI\,--config <pathname>\/\fR] [\fI\,--display <display>\/\fR] [\fI\,--no-xinitthreads\/\fR] [\fI\,--daemon|--no-daemon\/\fR] [\fI\,--name <screen-name>\/\fR] [\fI\,--restart|--no-restart\/\fR] [\fI\,--debug <level>\/\fR]
+.SH DESCRIPTION
+Start the barrier server component.
+.SH OPTIONS
+.TP
+\fB\-a\fR, \fB\-\-address\fR <address>
+listen for clients on the given address.
+.TP
+\fB\-c\fR, \fB\-\-config\fR <pathname>
+use the named configuration file instead.
+.TP
+\fB\-d\fR, \fB\-\-debug\fR <level>
+filter out log messages with priority below level.
+level may be: FATAL, ERROR, WARNING, NOTE, INFO,
+DEBUG, DEBUG1, DEBUG2.
+.TP
+\fB\-n\fR, \fB\-\-name\fR <screen\-name> use screen\-name instead the hostname to identify
+this screen in the configuration.
+.TP
+\fB\-1\fR, \fB\-\-no\-restart\fR
+do not try to restart on failure.
+.TP
+\fB\-\-restart\fR
+restart the server automatically if it fails. (*)
+.TP
+\fB\-l\fR \fB\-\-log\fR <file>
+write log messages to file.
+.TP
+\fB\-\-no\-tray\fR
+disable the system tray icon.
+.TP
+\fB\-\-enable\-drag\-drop\fR
+enable file drag & drop.
+.TP
+\fB\-\-enable\-crypto\fR
+enable the crypto (ssl) plugin.
+.TP
+\fB\-\-display\fR <display>
+connect to the X server at <display>
+.TP
+\fB\-\-no\-xinitthreads\fR
+do not call XInitThreads()
+.TP
+\fB\-f\fR, \fB\-\-no\-daemon\fR
+run in the foreground.
+.TP
+\fB\-\-daemon\fR
+run as a daemon. (*)
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+display this help and exit.
+.TP
+\fB\-\-version\fR
+display version information and exit.
+.PP
+Default options are marked with a *
+.PP
+The argument for \fB\-\-address\fR is of the form: [<hostname>][:<port>]. The
+hostname must be the address or hostname of an interface on the system.
+The default is to listen on all interfaces. The port overrides the
+default port, 24800.
+.PP
+If no configuration file pathname is provided then the first of the
+following to load successfully sets the configuration:
+.IP
+$HOME/.barrier.conf
+\fI\,/etc/barrier.conf\/\fP
+.SH COPYRIGHT
+Copyright \(co 2018 Debauchee Open Source Group
+.br
+Copyright \(co 2012\-2016 Symless Ltd.
+.br
+Copyright \(co 2008\-2014 Nick Bolton
+.br
+Copyright \(co 2002\-2014 Chris Schoeneman
diff --git a/doc/org.barrier-foss.org.barrierc.plist b/doc/org.barrier-foss.org.barrierc.plist
new file mode 100644
index 0000000..31e10ba
--- /dev/null
+++ b/doc/org.barrier-foss.org.barrierc.plist
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<!-- Mac OSX only: Copy this plist file into [~]/Library/LaunchAgents to start barrier client automatically. Make sure you change the IP below. -->
+<dict>
+ <key>Label</key>
+ <string>org.debauchee.com.barrierc.plist</string>
+ <key>OnDemand</key>
+ <false/>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/bin/barrierc</string>
+ <!-- Replace this IP with the IP of your barriers server -->
+ <string>192.168.0.2</string>
+ </array>
+ <key>RunAtLoad</key>
+ <true/>
+</dict>
+</plist>
diff --git a/doc/org.barrier-foss.org.barriers.plist b/doc/org.barrier-foss.org.barriers.plist
new file mode 100644
index 0000000..f1ab5bf
--- /dev/null
+++ b/doc/org.barrier-foss.org.barriers.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
+ "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<!-- Mac OSX only: Copy this plist file into [~]/Library/LaunchAgents to start barrier server automatically. Make sure you change configuration file below -->
+<dict>
+ <key>Label</key>
+ <string>org.debauchee.com.barriers.plist</string>
+ <key>OnDemand</key>
+ <false/>
+ <key>ProgramArguments</key>
+ <array>
+ <string>/usr/bin/barriers</string>
+ <string>--no-daemon</string>
+ <string>--config</string>
+ <!-- Replace this path with the path to your barrier configuration -->
+ <string>/Users/snorp/.barrier.conf</string>
+ </array>
+ <key>RunAtLoad</key>
+ <true/>
+</dict>
+</plist>
diff --git a/osx_environment.sh b/osx_environment.sh
new file mode 100644
index 0000000..f05ba58
--- /dev/null
+++ b/osx_environment.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+if [ ! $BARRIER_BUILD_ENV ]; then
+
+ printf "Modifying environment for Barrier build..."
+
+ QT_PATH=$(brew --prefix qt)
+ OPENSSL_PATH=$(brew --prefix openssl)
+
+ export CMAKE_PREFIX_PATH="$QT_PATH:$CMAKE_PREFIX_PATH"
+ export LD_LIBRARY_PATH="$OPENSSL_PATH/lib:$LD_LIBRARY_PATH"
+ export CPATH="$OPENSSL_PATH/include:$CPATH"
+ export PKG_CONFIG_PATH="$OPENSSL_PATH/lib/pkgconfig:$PKG_CONFIG_PATH"
+ export BARRIER_BUILD_ENV=1
+
+ printf "done\n"
+fi
diff --git a/res/License.rtf b/res/License.rtf
new file mode 100644
index 0000000..ea1602d
--- /dev/null
+++ b/res/License.rtf
@@ -0,0 +1,102 @@
+{\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf460
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\froman\fcharset0 Times-Roman;}
+{\colortbl;\red255\green255\blue255;}
+{\info
+{\title Original file was gpl-2.0.tex}
+{\doccomm Created using latex2rtf 1.9.19a on Sun Jul 12 19:21:22 2009}}\paperw12280\paperh15900\margl2680\margr2700\margb1760\margt2540\vieww12280\viewh15900\viewkind1
+\deftab720
+\pard\pardeftab720\ri0\qj
+
+\f0\fs24 \cf0 \
+\pard\pardeftab720\ri0\qc
+
+\f1\fs30 \cf0 GNU GENERAL PUBLIC LICENSE
+\f0\fs24 \
+\
+\
+\pard\pardeftab720\ri0\qc
+
+\f1 \cf0 Version 2, June 1991\
+\
+\
+Copyright \'a9 1989, 1991 Free Software Foundation, Inc.\
+\pard\pardeftab720\ri0\sb240\qc
+\cf0 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA\
+\pard\pardeftab720\ri0\qc
+\cf0 Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. \
+\pard\pardeftab720\ri0\qc
+
+\b\fs26 \cf0 Preamble
+\b0\fs24 \
+\pard\pardeftab720\ri0\qj
+\cf0 The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software\'97to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation\'92s software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.\
+When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.\
+To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.\
+For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.\
+We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.\
+Also, for each author\'92s protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors\'92 reputations.\
+Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone\'92s free use or not licensed at all.\
+The precise terms and conditions for copying, distribution and modification follow.\
+\pard\pardeftab720\ri0\qc
+
+\fs31 \cf0 Terms and Conditions For Copying, Distribution and Modification
+\fs24 \
+\pard\pardeftab720\li600\fi-300\ri0\sb50\qj
+\cf0 1. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The \'93Program\'94, below, refers to any such program or work, and a \'93work based on the Program\'94 means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term \'93modification\'94.) Each licensee is addressed as \'93you\'94.\
+\pard\pardeftab720\li600\ri0\qj
+\cf0 Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.\
+\pard\pardeftab720\li600\fi-300\ri0\sb50\qj
+\cf0 2. You may copy and distribute verbatim copies of the Program\'92s source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.\
+\pard\pardeftab720\li600\ri0\qj
+\cf0 You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.\
+\pard\pardeftab720\li600\fi-300\ri0\sb50\qj
+\cf0 3. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:\
+\pard\pardeftab720\li1200\fi-300\ri0\sb50\qj
+\cf0 (a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.\
+(b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.\
+(c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)\
+\pard\pardeftab720\li600\ri0\sb100\qj
+\cf0 These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.\
+\pard\pardeftab720\li600\fi-300\ri0\qj
+\cf0 Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.\
+In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.\
+\pard\pardeftab720\li600\fi-300\ri0\sb50\qj
+\cf0 4. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:\
+\pard\pardeftab720\li1200\fi-300\ri0\sb50\qj
+\cf0 (a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\
+(b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,\
+(c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)\
+\pard\pardeftab720\li600\ri0\sb100\qj
+\cf0 The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.\
+\pard\pardeftab720\li600\fi-300\ri0\qj
+\cf0 If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.\
+\pard\pardeftab720\li600\fi-300\ri0\sb50\qj
+\cf0 5. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.\
+6. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.\
+7. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients\'92 exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.\
+8. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.\
+\pard\pardeftab720\li600\ri0\qj
+\cf0 If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.\
+It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.\
+This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.\
+\pard\pardeftab720\li600\fi-300\ri0\sb50\qj
+\cf0 9. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.\
+10. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.\
+\pard\pardeftab720\li600\ri0\qj
+\cf0 Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and \'93any later version\'94, you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.\
+\pard\pardeftab720\li600\fi-300\ri0\sb50\qj
+\cf0 11. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.\
+\pard\pardeftab720\li600\ri0\qc
+
+\fs31 \cf0 No Warranty
+\fs24 \
+\pard\pardeftab720\li600\fi-300\ri0\sb50\qj
+\cf0 12. Because the program is licensed free of charge, there is no warranty for the program, to the extent permitted by applicable law. Except when otherwise stated in writing the copyright holders and/or other parties provide the program \'93as is\'94 without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The entire risk as to the quality and performance of the program is with you. Should the program prove defective, you assume the cost of all necessary servicing, repair or correction.
+\f0 \
+
+\f1 13. In no event unless required by applicable law or agreed to in writing will any copyright holder, or any other party who may modify and/or redistribute the program as permitted above, be liable to you for damages, including any general, special, incidental or consequential damages arising out of the use or inability to use the program (including but not limited to loss of data or data being rendered inaccurate or losses sustained by you or third parties or a failure of the program to operate with any other programs), even if such holder or other party has been advised of the possibility of such damages.
+\f0 \
+\pard\pardeftab720\ri0\sb100\qc
+
+\f1\fs31 \cf0 End of Terms and Conditions
+\fs24 } \ No newline at end of file
diff --git a/res/License.tex b/res/License.tex
new file mode 100644
index 0000000..560aa9c
--- /dev/null
+++ b/res/License.tex
@@ -0,0 +1,422 @@
+\documentclass[11pt]{article}
+
+\title{GNU GENERAL PUBLIC LICENSE}
+\date{Version 2, June 1991}
+
+\begin{document}
+\maketitle
+
+\begin{center}
+{\parindent 0in
+
+Copyright \copyright\ 1989, 1991 Free Software Foundation, Inc.
+
+\bigskip
+
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+\bigskip
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+}
+\end{center}
+
+\begin{center}
+{\bf\large Preamble}
+\end{center}
+
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software---to
+make sure the software is free for all its users. This General Public
+License applies to most of the Free Software Foundation's software and to
+any other program whose authors commit to using it. (Some other Free
+Software Foundation software is covered by the GNU Library General Public
+License instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price.
+Our General Public Licenses are designed to make sure that you have the
+freedom to distribute copies of free software (and charge for this service
+if you wish), that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free programs;
+and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to
+deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or
+for a fee, you must give the recipients all the rights that you have. You
+must make sure that they, too, receive or can get the source code. And
+you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If
+the software is modified by someone else and passed on, we want its
+recipients to know that what they have is not the original, so that any
+problems introduced by others will not reflect on the original authors'
+reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+\begin{center}
+{\Large \sc Terms and Conditions For Copying, Distribution and
+ Modification}
+\end{center}
+
+
+%\renewcommand{\theenumi}{\alpha{enumi}}
+\begin{enumerate}
+
+\addtocounter{enumi}{-1}
+
+\item
+
+This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the
+terms of this General Public License. The ``Program'', below, refers to
+any such program or work, and a ``work based on the Program'' means either
+the Program or any derivative work under copyright law: that is to say, a
+work containing the Program or a portion of it, either verbatim or with
+modifications and/or translated into another language. (Hereinafter,
+translation is included without limitation in the term ``modification''.)
+Each licensee is addressed as ``you''.
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+\item You may copy and distribute verbatim copies of the Program's source
+ code as you receive it, in any medium, provided that you conspicuously
+ and appropriately publish on each copy an appropriate copyright notice
+ and disclaimer of warranty; keep intact all the notices that refer to
+ this License and to the absence of any warranty; and give any other
+ recipients of the Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you
+may at your option offer warranty protection in exchange for a fee.
+
+\item
+
+You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+\begin{enumerate}
+
+\item
+
+You must cause the modified files to carry prominent notices stating that
+you changed the files and the date of any change.
+
+\item
+
+You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+\item
+If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+\end{enumerate}
+
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+\item
+You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+\begin{enumerate}
+
+\item
+
+Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+\item
+
+Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+\item
+
+Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+\end{enumerate}
+
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+\item
+You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+\item
+You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+\item
+Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+\item
+If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+\item
+If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+\item
+The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and ``any
+later version'', you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+\item
+If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+\begin{center}
+{\Large\sc
+No Warranty
+}
+\end{center}
+
+\item
+{\sc Because the program is licensed free of charge, there is no warranty
+for the program, to the extent permitted by applicable law. Except when
+otherwise stated in writing the copyright holders and/or other parties
+provide the program ``as is'' without warranty of any kind, either expressed
+or implied, including, but not limited to, the implied warranties of
+merchantability and fitness for a particular purpose. The entire risk as
+to the quality and performance of the program is with you. Should the
+program prove defective, you assume the cost of all necessary servicing,
+repair or correction.}
+
+\item
+{\sc In no event unless required by applicable law or agreed to in writing
+will any copyright holder, or any other party who may modify and/or
+redistribute the program as permitted above, be liable to you for damages,
+including any general, special, incidental or consequential damages arising
+out of the use or inability to use the program (including but not limited
+to loss of data or data being rendered inaccurate or losses sustained by
+you or third parties or a failure of the program to operate with any other
+programs), even if such holder or other party has been advised of the
+possibility of such damages.}
+
+\end{enumerate}
+
+
+\begin{center}
+{\Large\sc End of Terms and Conditions}
+\end{center}
+
+
+\pagebreak[2]
+
+\section*{Appendix: How to Apply These Terms to Your New Programs}
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+ To do so, attach the following notices to the program. It is safest to
+ attach them to the start of each source file to most effectively convey
+ the exclusion of warranty; and each file should have at least the
+ ``copyright'' line and a pointer to where the full notice is found.
+
+\begin{quote}
+one line to give the program's name and a brief idea of what it does. \\
+Copyright (C) yyyy name of author \\
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program 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, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+\end{quote}
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+\begin{quote}
+Gnomovision version 69, Copyright (C) yyyy name of author \\
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. \\
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+\end{quote}
+
+
+The hypothetical commands {\tt show w} and {\tt show c} should show the
+appropriate parts of the General Public License. Of course, the commands
+you use may be called something other than {\tt show w} and {\tt show c};
+they could even be mouse-clicks or menu items---whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a ``copyright disclaimer'' for the program, if
+necessary. Here is a sample; alter the names:
+
+\begin{quote}
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program \\
+`Gnomovision' (which makes passes at compilers) written by James Hacker. \\
+
+signature of Ty Coon, 1 April 1989 \\
+Ty Coon, President of Vice
+\end{quote}
+
+
+This General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications
+with the library. If this is what you want to do, use the GNU Library
+General Public License instead of this License.
+
+\end{document}
diff --git a/res/Readme.txt b/res/Readme.txt
new file mode 100644
index 0000000..0b2802b
--- /dev/null
+++ b/res/Readme.txt
@@ -0,0 +1,9 @@
+Thank you for chosing Barrier!
+https://github.com/debauchee/barrier/
+
+Barrier allows you to share your keyboard and mouse between computers over a network.
+
+Have fun!
+
+Thanks,
+The Barrier Team
diff --git a/res/banner.bmp b/res/banner.bmp
new file mode 100644
index 0000000..fa5c98f
--- /dev/null
+++ b/res/banner.bmp
Binary files differ
diff --git a/res/barrier.desktop b/res/barrier.desktop
new file mode 100644
index 0000000..94422ea
--- /dev/null
+++ b/res/barrier.desktop
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Type=Application
+Version=1.0
+Name=Barrier
+Comment=Keyboard and mouse sharing solution
+Path=/usr/bin
+Exec=/usr/bin/barrier
+Icon=barrier
+Terminal=false
+Categories=Utility;
+Keywords=keyboard;mouse;sharing;network;share;
+
diff --git a/res/barrier.ico b/res/barrier.ico
new file mode 100644
index 0000000..6e90545
--- /dev/null
+++ b/res/barrier.ico
Binary files differ
diff --git a/res/barrier.png b/res/barrier.png
new file mode 100644
index 0000000..3313c31
--- /dev/null
+++ b/res/barrier.png
Binary files differ
diff --git a/res/barrier.svg b/res/barrier.svg
new file mode 100644
index 0000000..5195bb1
--- /dev/null
+++ b/res/barrier.svg
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ viewBox="0 0 500 500"
+ style="enable-background:new 0 0 500 500;"
+ xml:space="preserve"
+ sodipodi:docname="barrier.svg"
+ inkscape:version="0.92.2 5c3e80d, 2017-08-06"
+ inkscape:export-filename="/home/ian/src/barrier.git/res/barrier.png"
+ inkscape:export-xdpi="49.150002"
+ inkscape:export-ydpi="49.150002"><metadata
+ id="metadata3769"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs3767" /><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1026"
+ id="namedview3765"
+ showgrid="false"
+ inkscape:zoom="1.1756822"
+ inkscape:cx="213.25905"
+ inkscape:cy="203.41561"
+ inkscape:window-x="0"
+ inkscape:window-y="30"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="Layer_1" /><style
+ type="text/css"
+ id="style3754">
+ .st0{fill:#3F96B9;}
+ .st1{fill:#5D7DBD;}
+ .st2{fill:#196260;}
+ .st3{fill:#97C33D;}
+</style><path
+ class="st1"
+ d="m 444.53004,299.96289 c -22.0044,4.33789 -43.4063,-9.8677 -47.6695,-31.6538 l -0.1128,0.0332 c -0.0195,-0.062 -0.0278,-0.0992 -0.0371,-0.1382 -14.43449,-73.3716 -79.4389,-124.1621 -151.6889,-123.3052 11.7719,-11.7851 17.68111,-29.07369 14.20551,-46.69869 -2.8974,-14.74901 -11.82761,-26.77251 -23.64789,-34.10301 113.92969,-5.5118 217.99118,73.3251 240.63478,188.48581 0.4448,2.2778 0.767,4.52639 1.1655,6.77488 0.4546,19.27732 -13.1216,36.72171 -32.8496,40.60501"
+ id="path3758"
+ inkscape:connector-curvature="0"
+ style="fill:#5d7dbd" /><path
+ class="st3"
+ d="m 8.2366396,352.42909 c -4.1397099,-21.121 8.8071004,-41.6318 29.1313004,-47.01071 1.1543,-0.22751 2.32961,-0.3594 3.4653,-0.58789 73.3194,-14.43262 124.0635,-79.36229 123.3248,-151.53611 11.77969,12.07421 29.2822,18.1924 47.1147,14.6787 14.5078,-2.83988 26.3838,-11.51659 33.749,-23.07419 4.876,113.4316 -73.7993,216.84859 -188.55219,239.4258 -1.67681,0.30268 -3.32471,0.56928 -4.9922,0.85248 -20.2661,1.8389 -39.16941,-12.14059 -43.2407104,-32.74808"
+ id="path3762"
+ inkscape:connector-curvature="0"
+ style="fill:#97c33d" /><path
+ class="st0"
+ d="m 196.90869,356.69147 c 21.74407,4.28031 42.98725,-10.13468 47.31436,-32.13466 3.97759,-20.209 -7.7089,-39.89847 -26.5175,-46.26266 -1.8194,-0.26571 -3.62598,-0.5869 -5.41698,-0.92779 C 139.32964,263.00706 88.700776,198.6477 88.946866,126.89879 77.203686,138.85086 59.776886,144.85578 42.048406,141.35087 27.398016,138.47297 15.438136,129.64679 8.0875262,117.92219 3.4283464,231.17885 82.070866,334.34191 196.66358,356.87018 c 0.0467,0.0186 0.13284,0.0186 0.207,0.0186 z"
+ id="path3756"
+ inkscape:connector-curvature="0"
+ style="fill:#3f96b9;stroke-width:0.99999911" /><path
+ class="st2"
+ d="m 454.25179,123.03077 c 20.22858,-1.794 39.0743,12.1518 43.10939,32.7309 4.1568,21.18071 -8.8369,41.7407 -29.2256,47.0825 -0.6445,0.10751 -1.26071,0.1739 -1.875,0.2979 -73.01749,14.3657 -123.68451,78.8266 -123.3252,150.6474 -11.78118,-11.62689 -28.97949,-17.42619 -46.48138,-13.9848 -14.9053,2.90821 -27.04593,12.00492 -34.32031,24.0132 -5.28421,-113.72659 73.49121,-217.57908 188.4902,-240.18509 1.2031,-0.23191 2.4151,-0.37741 3.6279,-0.60201"
+ id="path3760"
+ inkscape:connector-curvature="0"
+ style="fill:#196260" /><path
+ style="fill:#333333;stroke-width:1.05979359"
+ d="m -66.847656,21.962891 c -1.14505,-0.03744 -2.273167,0.278796 -3.386719,0.78125 h 6.320313 c -0.983425,-0.440945 -1.963156,-0.749518 -2.933594,-0.78125 z"
+ id="path3756-3"
+ inkscape:connector-curvature="0" /><path
+ sodipodi:type="spiral"
+ style="fill:#5d7dbd;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.5"
+ id="path3860"
+ sodipodi:cx="301.11868"
+ sodipodi:cy="391.77664"
+ sodipodi:expansion="1"
+ sodipodi:revolution="3"
+ sodipodi:radius="76.230827"
+ sodipodi:argument="-18.215923"
+ sodipodi:t0="0"
+ d="m 301.11868,391.77664 c 3.08016,2.26297 -1.53079,5.26846 -3.7612,5.11942 -6.04427,-0.40388 -8.12919,-7.74783 -6.47764,-12.64183 2.95423,-8.7542 13.70234,-11.37066 21.52246,-7.83585 11.47633,5.18747 14.69271,19.73203 9.19407,30.40308 -7.32883,14.22283 -25.78767,18.04817 -39.28371,10.55229 -16.98339,-9.43281 -21.42072,-31.8555 -11.9105,-48.16433 11.51814,-19.75219 37.93015,-24.80314 57.04495,-13.26873 22.52614,13.59291 28.19173,44.00907 14.62694,65.92559 -15.66109,25.30348 -50.09081,31.58445 -74.80621,15.98515 -28.0832,-17.72491 -34.98006,-56.17456 -17.34337,-83.68683 19.7857,-30.86461 62.25977,-38.37778 92.56746,-18.70159 33.64729,21.84429 41.77706,68.34606 20.05981,101.44808" /><path
+ sodipodi:type="spiral"
+ style="fill:#3f96b9;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.20000005"
+ id="path3860-3"
+ sodipodi:cx="-206.4883"
+ sodipodi:cy="-105.78166"
+ sodipodi:expansion="1"
+ sodipodi:revolution="3"
+ sodipodi:radius="60.984657"
+ sodipodi:argument="-18.215923"
+ sodipodi:t0="0"
+ d="m -206.4883,-105.78166 c 2.46413,1.81037 -1.22463,4.21476 -3.00896,4.09553 -4.83541,-0.3231 -6.50335,-6.19826 -5.18211,-10.11346 2.36338,-7.00336 10.96187,-9.09653 17.21796,-6.26868 9.18107,4.14997 11.75417,15.78562 7.35526,24.322461 -5.86306,11.378267 -20.63013,14.438538 -31.42696,8.441831 -13.58671,-7.546245 -17.13658,-25.484392 -9.52841,-38.531462 9.21452,-15.80175 30.34412,-19.84251 45.63597,-10.61498 18.0209,10.87432 22.55338,35.207251 11.70155,52.740464 -12.52887,20.242788 -40.07265,25.267561 -59.84497,12.788124 -22.46655,-14.179929 -27.98405,-44.939648 -13.87469,-66.949458 15.82856,-24.69169 49.80781,-30.70222 74.05396,-14.96128 26.91783,17.47544 33.42164,54.676853 16.04784,81.158467"
+ transform="scale(-1)" /><path
+ sodipodi:type="spiral"
+ style="fill:#196260;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.80000001"
+ id="path3860-3-5"
+ sodipodi:cx="46.779533"
+ sodipodi:cy="-92.928612"
+ sodipodi:expansion="1"
+ sodipodi:revolution="3"
+ sodipodi:radius="40.656441"
+ sodipodi:argument="-18.215923"
+ sodipodi:t0="0"
+ d="m 46.779533,-92.928612 c 1.64275,1.206917 -0.816425,2.809844 -2.005975,2.730358 -3.223608,-0.215402 -4.335565,-4.13218 -3.454741,-6.742309 1.575589,-4.668907 7.307917,-6.064347 11.478643,-4.179127 6.120709,2.766654 7.836115,10.523753 4.903505,16.214981 -3.908707,7.585512 -13.753422,9.625693 -20.95131,5.627887 -9.057807,-5.03083 -11.424386,-16.989597 -6.352269,-25.687648 6.143012,-10.5345 20.229415,-13.22833 30.423977,-7.07665 12.013938,7.24955 15.035589,23.471504 7.801034,35.160313 -8.35258,13.495193 -26.715102,16.845042 -39.896644,8.525417 -14.977707,-9.453287 -18.656035,-29.959766 -9.249799,-44.63298 10.552374,-16.46112 33.205208,-20.46814 49.369311,-9.97418 17.945219,11.65029 22.281096,36.451235 10.698564,54.105646"
+ transform="scale(1,-1)" /></svg> \ No newline at end of file
diff --git a/res/barrier2.desktop b/res/barrier2.desktop
new file mode 100644
index 0000000..25aaf51
--- /dev/null
+++ b/res/barrier2.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Type=Application
+Version=1.0
+Name=Barrier
+Comment=Keyboard and mouse sharing solution
+Path=/usr/bin
+Exec=/usr/bin/barrier2
+Icon=barrier
+Terminal=false
+Categories=Utility;
+Keywords=keyboard;mouse;sharing;network;share;
diff --git a/res/config.h.in b/res/config.h.in
new file mode 100644
index 0000000..f3a7244
--- /dev/null
+++ b/res/config.h.in
@@ -0,0 +1,173 @@
+/* Define version here for Unix, but using /D for Windows. */
+#cmakedefine BARRIER_VERSION "${BARRIER_VERSION}"
+
+/* Define to the base type of arg 3 for `accept`. */
+#cmakedefine ACCEPT_TYPE_ARG3 ${ACCEPT_TYPE_ARG3}
+
+/* Define if your compiler has bool support. */
+#cmakedefine HAVE_CXX_BOOL ${HAVE_CXX_BOOL}
+
+/* Define if your compiler has C++ cast support. */
+#cmakedefine HAVE_CXX_CASTS ${HAVE_CXX_CASTS}
+
+/* Define if your compiler has exceptions support. */
+#cmakedefine HAVE_CXX_EXCEPTIONS ${HAVE_CXX_EXCEPTIONS}
+
+/* Define if your compiler has mutable support. */
+#cmakedefine HAVE_CXX_MUTABLE ${HAVE_CXX_MUTABLE}
+
+/* Define if your compiler has standard C++ library support. */
+#cmakedefine HAVE_CXX_STDLIB ${HAVE_CXX_STDLIB}
+
+/* Define if the <X11/extensions/dpms.h> header file declares function prototypes. */
+#cmakedefine HAVE_DPMS_PROTOTYPES ${HAVE_DPMS_PROTOTYPES}
+
+/* Define if you have a working `getpwuid_r` function. */
+#cmakedefine HAVE_GETPWUID_R ${HAVE_GETPWUID_R}
+
+/* Define to 1 if you have the `gmtime_r` function. */
+#cmakedefine HAVE_GMTIME_R ${HAVE_GMTIME_R}
+
+/* Define if you have the `inet_aton` function. */
+#cmakedefine HAVE_INET_ATON ${HAVE_INET_ATON}
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#cmakedefine HAVE_INTTYPES_H ${HAVE_INTTYPES_H}
+
+/* Define to 1 if you have the <istream> header file. */
+#cmakedefine HAVE_ISTREAM ${HAVE_ISTREAM}
+
+/* Define to 1 if you have the <locale.h> header file. */
+#cmakedefine HAVE_LOCALE_H ${HAVE_LOCALE_H}
+
+/* Define to 1 if you have the <memory.h> header file. */
+#cmakedefine HAVE_MEMORY_H ${HAVE_MEMORY_H}
+
+/* Define if you have the `nanosleep` function. */
+#cmakedefine HAVE_NANOSLEEP ${HAVE_NANOSLEEP}
+
+/* Define to 1 if you have the <ostream> header file. */
+#cmakedefine HAVE_OSTREAM ${HAVE_OSTREAM}
+
+/* Define if you have the `poll` function. */
+#cmakedefine HAVE_POLL ${HAVE_POLL}
+
+/* Define if you have a POSIX `sigwait` function. */
+#cmakedefine HAVE_POSIX_SIGWAIT ${HAVE_POSIX_SIGWAIT}
+
+/* Define if you have POSIX threads libraries and header files. */
+#cmakedefine HAVE_PTHREAD ${HAVE_PTHREAD}
+
+/* Define if you have `pthread_sigmask` and `pthread_kill` functions. */
+#cmakedefine HAVE_PTHREAD_SIGNAL ${HAVE_PTHREAD_SIGNAL}
+
+/* Define if your compiler defines socklen_t. */
+#cmakedefine HAVE_SOCKLEN_T ${HAVE_SOCKLEN_T}
+
+/* Define to 1 if you have the <sstream> header file. */
+#cmakedefine HAVE_SSTREAM ${HAVE_SSTREAM}
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#cmakedefine HAVE_STDINT_H ${HAVE_STDINT_H}
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#cmakedefine HAVE_STDLIB_H ${HAVE_STDLIB_H}
+
+/* Define to 1 if you have the `strftime` function. */
+#cmakedefine HAVE_STRFTIME ${HAVE_STRFTIME}
+
+/* Define to 1 if you have the <strings.h> header file. */
+#cmakedefine HAVE_STRINGS_H ${HAVE_STRINGS_H}
+
+/* Define to 1 if you have the <string.h> header file. */
+#cmakedefine HAVE_STRING_H ${HAVE_STRING_H}
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#cmakedefine HAVE_SYS_SELECT_H ${HAVE_SYS_SELECT_H}
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#cmakedefine HAVE_SYS_SOCKET_H ${HAVE_SYS_SOCKET_H}
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#cmakedefine HAVE_SYS_STAT_H ${HAVE_SYS_STAT_H}
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#cmakedefine HAVE_SYS_TIME_H ${HAVE_SYS_TIME_H}
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#cmakedefine HAVE_SYS_TYPES_H ${HAVE_SYS_TYPES_H}
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#cmakedefine HAVE_SYS_UTSNAME_H ${HAVE_SYS_UTSNAME_H}
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#cmakedefine HAVE_UNISTD_H ${HAVE_UNISTD_H}
+
+/* Define to 1 if you have the `vsnprintf` function. */
+#cmakedefine HAVE_VSNPRINTF ${HAVE_VSNPRINTF}
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#cmakedefine HAVE_WCHAR_H ${HAVE_WCHAR_H}
+
+/* Define to 1 if you have the <X11/extensions/Xrandr.h> header file. */
+#cmakedefine HAVE_X11_EXTENSIONS_XRANDR_H ${HAVE_X11_EXTENSIONS_XRANDR_H}
+
+/* Define to 1 if you have the <X11/extensions/dpms.h> header file. */
+#cmakedefine HAVE_X11_EXTENSIONS_DPMS_H ${HAVE_X11_EXTENSIONS_DPMS_H}
+
+/* Define to 1 if you have the <X11/extensions/Xinerama.h> header file. */
+#cmakedefine HAVE_X11_EXTENSIONS_XINERAMA_H ${HAVE_X11_EXTENSIONS_XINERAMA_H}
+
+/* Define to 1 if you have the <X11/extensions/XKBstr.h> header file. */
+#cmakedefine HAVE_X11_EXTENSIONS_XKBSTR_H ${HAVE_X11_EXTENSIONS_XKBSTR_H}
+
+/* Define to 1 if you have the <X11/extensions/XTest.h> header file. */
+#cmakedefine HAVE_X11_EXTENSIONS_XTEST_H ${HAVE_X11_EXTENSIONS_XTEST_H}
+
+/* Define to 1 if you have the <X11/XKBlib.h> header file. */
+#cmakedefine HAVE_X11_XKBLIB_H ${HAVE_X11_XKBLIB_H}
+
+/* Define to 1 if you have the <X11/extensions/XInput2.h> header file. */
+#cmakedefine HAVE_XI2 ${HAVE_XI2}
+
+/* Define this if the XKB extension is available. */
+#cmakedefine HAVE_XKB_EXTENSION ${HAVE_XKB_EXTENSION}
+
+/* Define to necessary symbol if this constant uses a non-standard name on your system. */
+#cmakedefine PTHREAD_CREATE_JOINABLE ${PTHREAD_CREATE_JOINABLE}
+
+/* Define to the type of arg 1 for `select`. */
+#cmakedefine SELECT_TYPE_ARG1 ${SELECT_TYPE_ARG1}
+
+/* Define to the type of args 2, 3 and 4 for `select`. */
+#cmakedefine SELECT_TYPE_ARG234 ${SELECT_TYPE_ARG234}
+
+/* Define to the type of arg 5 for `select`. */
+#cmakedefine SELECT_TYPE_ARG5 ${SELECT_TYPE_ARG5}
+
+/* The size of `char`, as computed by sizeof. */
+#cmakedefine SIZEOF_CHAR ${SIZEOF_CHAR}
+
+/* The size of `int`, as computed by sizeof. */
+#cmakedefine SIZEOF_INT ${SIZEOF_INT}
+
+/* The size of `long`, as computed by sizeof. */
+#cmakedefine SIZEOF_LONG ${SIZEOF_LONG}
+
+/* The size of `short`, as computed by sizeof. */
+#cmakedefine SIZEOF_SHORT ${SIZEOF_SHORT}
+
+/* Define to 1 if you have the ANSI C header files. */
+#cmakedefine STDC_HEADERS ${STDC_HEADERS}
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#cmakedefine TIME_WITH_SYS_TIME ${TIME_WITH_SYS_TIME}
+
+/* Define to 1 if your <sys/time.h> declares `struct tm`. */
+#cmakedefine TM_IN_SYS_TIME ${TM_IN_SYS_TIME}
+
+/* Define to 1 if the X Window System is missing or not being used. */
+#cmakedefine X_DISPLAY_MISSING ${X_DISPLAY_MISSING}
+
+/* Define to `unsigned int` if <sys/types.h> does not define. */
+#cmakedefine size_t ${size_t}
diff --git a/res/dialog.bmp b/res/dialog.bmp
new file mode 100644
index 0000000..ad588c2
--- /dev/null
+++ b/res/dialog.bmp
Binary files differ
diff --git a/res/doxygen.cfg.in b/res/doxygen.cfg.in
new file mode 100644
index 0000000..e8233a2
--- /dev/null
+++ b/res/doxygen.cfg.in
@@ -0,0 +1,1635 @@
+# Doxyfile 1.7.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = "Barrier"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = "${VERSION}"
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc/doxygen
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penality.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will rougly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+#SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+#SHOW_DIRECTORIES = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = YES
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = NO
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT =
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = .
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.cpp \
+ *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS = */.svn/* \
+ */.git/* \
+ */ext/* \
+ */src/gui/* \
+ */src/test/*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = YES
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 3
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT =
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the stylesheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
+
+HTML_TIMESTAMP = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+#HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+#USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvances is that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT =
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = NO
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = NO
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT =
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT =
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION =
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED =
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = @HAVE_DOT@
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+#DOT_FONTNAME = FreeSans.ttf
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
diff --git a/res/makeicon.sh b/res/makeicon.sh
new file mode 100755
index 0000000..2883755
--- /dev/null
+++ b/res/makeicon.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+ICNS_BASE=../dist/macos/bundle/Barrier.app/Contents/Resources
+if ! which magick >/dev/null 2>&1; then
+ echo "Need ImageMagic for this"
+ exit 10
+fi
+cd $(dirname $0) || exit $?
+if [ ! -r barrier.png ]; then
+ echo "Use inkscape (or another vector graphics editor) to create barrier.png from barrier.svg first"
+ exit 10
+fi
+rm -rf work || exit $?
+mkdir -p work || exit $?
+for s in 16 24 32 48 64 128 256 512 1024; do
+ magick convert barrier.png -resize ${s}x${s} -depth 8 work/${s}.png || exit $?
+done
+# windows icon
+magick convert work/{16,24,32,48,64,128}.png barrier.png barrier.ico || exit $?
+# macos icon
+png2icns $ICNS_BASE/Barrier.icns work/{16,32,256,512,1024}.png || exit $?
+rm -rf work
+echo Done
diff --git a/res/openssl/barrier.conf b/res/openssl/barrier.conf
new file mode 100644
index 0000000..84325c6
--- /dev/null
+++ b/res/openssl/barrier.conf
@@ -0,0 +1,65 @@
+#
+# Barrier OpenSSL configuration file.
+# Used for generation of certificate requests.
+#
+
+dir = .
+
+[ca]
+default_ca = CA_default
+
+[CA_default]
+serial = $dir/serial
+database = $dir/certindex.txt
+new_certs_dir = $dir/certs
+certificate = $dir/cacert.pem
+private_key = $dir/private/cakey.pem
+default_days = 365
+default_md = md5
+preserve = no
+email_in_dn = no
+nameopt = default_ca
+certopt = default_ca
+policy = policy_match
+
+[policy_match]
+countryName = match
+stateOrProvinceName = match
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[req]
+default_bits = 1024 # Size of keys
+default_keyfile = key.pem # name of generated keys
+default_md = md5 # message digest algorithm
+string_mask = nombstr # permitted characters
+distinguished_name = req_distinguished_name
+req_extensions = v3_req
+
+[req_distinguished_name]
+0.organizationName = Organization Name (company)
+organizationalUnitName = Organizational Unit Name (department, division)
+emailAddress = Email Address
+emailAddress_max = 40
+localityName = Locality Name (city, district)
+stateOrProvinceName = State or Province Name (full name)
+countryName = Country Name (2 letter code)
+countryName_min = 2
+countryName_max = 2
+commonName = Common Name (hostname, IP, or your name)
+commonName_max = 64
+0.organizationName_default = My Company
+localityName_default = My Town
+stateOrProvinceName_default = State or Providence
+countryName_default = US
+
+[v3_ca]
+basicConstraints = CA:TRUE
+subjectKeyIdentifier = hash
+authorityKeyIdentifier = keyid:always,issuer:always
+
+[v3_req]
+basicConstraints = CA:FALSE
+subjectKeyIdentifier = hash
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..3a8e83e
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,25 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2011 Nick Bolton
+#
+# 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_directories (./lib)
+include_directories (${CMAKE_CURRENT_BINARY_DIR}/lib)
+
+add_subdirectory(lib)
+add_subdirectory(cmd)
+
+if (BARRIER_BUILD_GUI)
+ add_subdirectory(gui)
+endif()
diff --git a/src/cmd/CMakeLists.txt b/src/cmd/CMakeLists.txt
new file mode 100644
index 0000000..0fdfe33
--- /dev/null
+++ b/src/cmd/CMakeLists.txt
@@ -0,0 +1,24 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2011 Nick Bolton
+#
+# 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/>.
+
+add_subdirectory(barrierc)
+add_subdirectory(barriers)
+add_subdirectory(syntool)
+
+if (WIN32)
+ add_subdirectory(barrierd)
+endif()
+
diff --git a/src/cmd/barrierc/.gitignore b/src/cmd/barrierc/.gitignore
new file mode 100644
index 0000000..41a58c4
--- /dev/null
+++ b/src/cmd/barrierc/.gitignore
@@ -0,0 +1 @@
+/*.aps
diff --git a/src/cmd/barrierc/CMakeLists.txt b/src/cmd/barrierc/CMakeLists.txt
new file mode 100644
index 0000000..92276d2
--- /dev/null
+++ b/src/cmd/barrierc/CMakeLists.txt
@@ -0,0 +1,57 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+set(sources
+ barrierc.cpp
+)
+
+if (WIN32)
+ file(GLOB arch_headers "MSWindows*.h")
+ file(GLOB arch_sources "MSWindows*.cpp")
+ list(APPEND sources
+ resource.h
+ barrierc.ico
+ barrierc.rc
+ tb_error.ico
+ tb_idle.ico
+ tb_run.ico
+ tb_wait.ico
+ )
+elseif (APPLE)
+ file(GLOB arch_headers "OSX*.h")
+ file(GLOB arch_sources "OSX*.cpp")
+elseif (UNIX)
+ file(GLOB arch_headers "XWindows*.h")
+ file(GLOB arch_sources "XWindows*.cpp")
+endif()
+
+list(APPEND sources ${arch_sources})
+list(APPEND headers ${arch_headers})
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_executable(barrierc ${sources})
+target_link_libraries(barrierc
+ arch base client common io mt net ipc platform server synlib ${libs} ${OPENSSL_LIBS})
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ install (TARGETS barrierc DESTINATION ${BARRIER_BUNDLE_BINARY_DIR})
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ install (TARGETS barrierc DESTINATION bin)
+endif()
+
diff --git a/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.cpp b/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.cpp
new file mode 100644
index 0000000..8d17900
--- /dev/null
+++ b/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.cpp
@@ -0,0 +1,376 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "MSWindowsClientTaskBarReceiver.h"
+
+#include "resource.h"
+#include "client/Client.h"
+#include "platform/MSWindowsClipboard.h"
+#include "platform/MSWindowsScreen.h"
+#include "arch/win32/ArchTaskBarWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/Arch.h"
+#include "base/EventQueue.h"
+#include "base/log_outputters.h"
+#include "base/EventTypes.h"
+
+//
+// MSWindowsClientTaskBarReceiver
+//
+
+const UINT MSWindowsClientTaskBarReceiver::s_stateToIconID[kMaxState] =
+{
+ IDI_TASKBAR_NOT_RUNNING,
+ IDI_TASKBAR_NOT_WORKING,
+ IDI_TASKBAR_NOT_CONNECTED,
+ IDI_TASKBAR_NOT_CONNECTED,
+ IDI_TASKBAR_CONNECTED
+};
+
+MSWindowsClientTaskBarReceiver::MSWindowsClientTaskBarReceiver(
+ HINSTANCE appInstance, const BufferedLogOutputter* logBuffer, IEventQueue* events) :
+ ClientTaskBarReceiver(events),
+ m_appInstance(appInstance),
+ m_window(NULL),
+ m_logBuffer(logBuffer)
+{
+ for (UInt32 i = 0; i < kMaxState; ++i) {
+ m_icon[i] = loadIcon(s_stateToIconID[i]);
+ }
+ m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR));
+
+ // don't create the window yet. we'll create it on demand. this
+ // has the side benefit of being created in the thread used for
+ // the task bar. that's good because it means the existence of
+ // the window won't prevent changing the main thread's desktop.
+
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+MSWindowsClientTaskBarReceiver::~MSWindowsClientTaskBarReceiver()
+{
+ cleanup();
+}
+
+void
+MSWindowsClientTaskBarReceiver::cleanup()
+{
+ ARCH->removeReceiver(this);
+ for (UInt32 i = 0; i < kMaxState; ++i) {
+ deleteIcon(m_icon[i]);
+ }
+ DestroyMenu(m_menu);
+ destroyWindow();
+}
+
+void
+MSWindowsClientTaskBarReceiver::showStatus()
+{
+ // create the window
+ createWindow();
+
+ // lock self while getting status
+ lock();
+
+ // get the current status
+ std::string status = getToolTip();
+
+ // done getting status
+ unlock();
+
+ // update dialog
+ HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS);
+ SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str());
+
+ if (!IsWindowVisible(m_window)) {
+ // position it by the mouse
+ POINT cursorPos;
+ GetCursorPos(&cursorPos);
+ RECT windowRect;
+ GetWindowRect(m_window, &windowRect);
+ int x = cursorPos.x;
+ int y = cursorPos.y;
+ int fw = GetSystemMetrics(SM_CXDLGFRAME);
+ int fh = GetSystemMetrics(SM_CYDLGFRAME);
+ int ww = windowRect.right - windowRect.left;
+ int wh = windowRect.bottom - windowRect.top;
+ int sw = GetSystemMetrics(SM_CXFULLSCREEN);
+ int sh = GetSystemMetrics(SM_CYFULLSCREEN);
+ if (fw < 1) {
+ fw = 1;
+ }
+ if (fh < 1) {
+ fh = 1;
+ }
+ if (x + ww - fw > sw) {
+ x -= ww - fw;
+ }
+ else {
+ x -= fw;
+ }
+ if (x < 0) {
+ x = 0;
+ }
+ if (y + wh - fh > sh) {
+ y -= wh - fh;
+ }
+ else {
+ y -= fh;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh,
+ SWP_SHOWWINDOW);
+ }
+}
+
+void
+MSWindowsClientTaskBarReceiver::runMenu(int x, int y)
+{
+ // do popup menu. we need a window to pass to TrackPopupMenu().
+ // the SetForegroundWindow() and SendMessage() calls around
+ // TrackPopupMenu() are to get the menu to be dismissed when
+ // another window gets activated and are just one of those
+ // win32 weirdnesses.
+ createWindow();
+ SetForegroundWindow(m_window);
+ HMENU menu = GetSubMenu(m_menu, 0);
+ SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE);
+ HMENU logLevelMenu = GetSubMenu(menu, 3);
+ CheckMenuRadioItem(logLevelMenu, 0, 6,
+ CLOG->getFilter() - kERROR, MF_BYPOSITION);
+ int n = TrackPopupMenu(menu,
+ TPM_NONOTIFY |
+ TPM_RETURNCMD |
+ TPM_LEFTBUTTON |
+ TPM_RIGHTBUTTON,
+ x, y, 0, m_window, NULL);
+ SendMessage(m_window, WM_NULL, 0, 0);
+
+ // perform the requested operation
+ switch (n) {
+ case IDC_TASKBAR_STATUS:
+ showStatus();
+ break;
+
+ case IDC_TASKBAR_LOG:
+ copyLog();
+ break;
+
+ case IDC_TASKBAR_SHOW_LOG:
+ ARCH->showConsole(true);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_ERROR:
+ CLOG->setFilter(kERROR);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_WARNING:
+ CLOG->setFilter(kWARNING);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_NOTE:
+ CLOG->setFilter(kNOTE);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_INFO:
+ CLOG->setFilter(kINFO);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG:
+ CLOG->setFilter(kDEBUG);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG1:
+ CLOG->setFilter(kDEBUG1);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG2:
+ CLOG->setFilter(kDEBUG2);
+ break;
+
+ case IDC_TASKBAR_QUIT:
+ quit();
+ break;
+ }
+}
+
+void
+MSWindowsClientTaskBarReceiver::primaryAction()
+{
+ showStatus();
+}
+
+const IArchTaskBarReceiver::Icon
+MSWindowsClientTaskBarReceiver::getIcon() const
+{
+ return static_cast<Icon>(m_icon[getStatus()]);
+}
+
+void
+MSWindowsClientTaskBarReceiver::copyLog() const
+{
+ if (m_logBuffer != NULL) {
+ // collect log buffer
+ String data;
+ for (BufferedLogOutputter::const_iterator index = m_logBuffer->begin();
+ index != m_logBuffer->end(); ++index) {
+ data += *index;
+ data += "\n";
+ }
+
+ // copy log to clipboard
+ if (!data.empty()) {
+ MSWindowsClipboard clipboard(m_window);
+ clipboard.open(0);
+ clipboard.emptyUnowned();
+ clipboard.add(IClipboard::kText, data);
+ clipboard.close();
+ }
+ }
+}
+
+void
+MSWindowsClientTaskBarReceiver::onStatusChanged()
+{
+ if (IsWindowVisible(m_window)) {
+ showStatus();
+ }
+}
+
+HICON
+MSWindowsClientTaskBarReceiver::loadIcon(UINT id)
+{
+ HANDLE icon = LoadImage(m_appInstance,
+ MAKEINTRESOURCE(id),
+ IMAGE_ICON,
+ 0, 0,
+ LR_DEFAULTCOLOR);
+ return static_cast<HICON>(icon);
+}
+
+void
+MSWindowsClientTaskBarReceiver::deleteIcon(HICON icon)
+{
+ if (icon != NULL) {
+ DestroyIcon(icon);
+ }
+}
+
+void
+MSWindowsClientTaskBarReceiver::createWindow()
+{
+ // ignore if already created
+ if (m_window != NULL) {
+ return;
+ }
+
+ // get the status dialog
+ m_window = CreateDialogParam(m_appInstance,
+ MAKEINTRESOURCE(IDD_TASKBAR_STATUS),
+ NULL,
+ (DLGPROC)&MSWindowsClientTaskBarReceiver::staticDlgProc,
+ reinterpret_cast<LPARAM>(
+ static_cast<void*>(this)));
+
+ // window should appear on top of everything, including (especially)
+ // the task bar.
+ LONG_PTR style = GetWindowLongPtr(m_window, GWL_EXSTYLE);
+ style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
+ SetWindowLongPtr(m_window, GWL_EXSTYLE, style);
+
+ // tell the task bar about this dialog
+ ArchTaskBarWindows::addDialog(m_window);
+}
+
+void
+MSWindowsClientTaskBarReceiver::destroyWindow()
+{
+ if (m_window != NULL) {
+ ArchTaskBarWindows::removeDialog(m_window);
+ DestroyWindow(m_window);
+ m_window = NULL;
+ }
+}
+
+BOOL
+MSWindowsClientTaskBarReceiver::dlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ // use default focus
+ return TRUE;
+
+ case WM_ACTIVATE:
+ // hide when another window is activated
+ if (LOWORD(wParam) == WA_INACTIVE) {
+ ShowWindow(hwnd, SW_HIDE);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+BOOL CALLBACK
+MSWindowsClientTaskBarReceiver::staticDlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ // if msg is WM_INITDIALOG, extract the MSWindowsClientTaskBarReceiver*
+ // and put it in the extra window data then forward the call.
+ MSWindowsClientTaskBarReceiver* self = NULL;
+ if (msg == WM_INITDIALOG) {
+ self = static_cast<MSWindowsClientTaskBarReceiver*>(
+ reinterpret_cast<void*>(lParam));
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) lParam);
+ }
+ else {
+ // get the extra window data and forward the call
+ LONG_PTR data = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (data != 0) {
+ self = (MSWindowsClientTaskBarReceiver*) data;
+ }
+ }
+
+ // forward the message
+ if (self != NULL) {
+ return self->dlgProc(hwnd, msg, wParam, lParam);
+ }
+ else {
+ return (msg == WM_INITDIALOG) ? TRUE : FALSE;
+ }
+}
+
+IArchTaskBarReceiver*
+createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events)
+{
+ ArchMiscWindows::setIcons(
+ (HICON)LoadImage(ArchMiscWindows::instanceWin32(),
+ MAKEINTRESOURCE(IDI_BARRIER),
+ IMAGE_ICON,
+ 32, 32, LR_SHARED),
+ (HICON)LoadImage(ArchMiscWindows::instanceWin32(),
+ MAKEINTRESOURCE(IDI_BARRIER),
+ IMAGE_ICON,
+ 16, 16, LR_SHARED));
+
+ return new MSWindowsClientTaskBarReceiver(
+ MSWindowsScreen::getWindowInstance(), logBuffer, events);
+}
diff --git a/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.h b/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.h
new file mode 100644
index 0000000..91688e8
--- /dev/null
+++ b/src/cmd/barrierc/MSWindowsClientTaskBarReceiver.h
@@ -0,0 +1,68 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/ClientTaskBarReceiver.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+class BufferedLogOutputter;
+class IEventQueue;
+
+//! Implementation of ClientTaskBarReceiver for Microsoft Windows
+class MSWindowsClientTaskBarReceiver : public ClientTaskBarReceiver {
+public:
+ MSWindowsClientTaskBarReceiver(HINSTANCE, const BufferedLogOutputter*, IEventQueue* events);
+ virtual ~MSWindowsClientTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+ void cleanup();
+
+protected:
+ void copyLog() const;
+
+ // ClientTaskBarReceiver overrides
+ virtual void onStatusChanged();
+
+private:
+ HICON loadIcon(UINT);
+ void deleteIcon(HICON);
+ void createWindow();
+ void destroyWindow();
+
+ BOOL dlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam);
+ static BOOL CALLBACK
+ staticDlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam);
+
+private:
+ HINSTANCE m_appInstance;
+ HWND m_window;
+ HMENU m_menu;
+ HICON m_icon[kMaxState];
+ const BufferedLogOutputter* m_logBuffer;
+
+ static const UINT s_stateToIconID[];
+};
diff --git a/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp b/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp
new file mode 100644
index 0000000..7e79991
--- /dev/null
+++ b/src/cmd/barrierc/OSXClientTaskBarReceiver.cpp
@@ -0,0 +1,69 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "OSXClientTaskBarReceiver.h"
+#include "arch/Arch.h"
+
+//
+// OSXClientTaskBarReceiver
+//
+
+OSXClientTaskBarReceiver::OSXClientTaskBarReceiver(
+ const BufferedLogOutputter*,
+ IEventQueue* events) :
+ ClientTaskBarReceiver(events)
+{
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+OSXClientTaskBarReceiver::~OSXClientTaskBarReceiver()
+{
+ ARCH->removeReceiver(this);
+}
+
+void
+OSXClientTaskBarReceiver::showStatus()
+{
+ // do nothing
+}
+
+void
+OSXClientTaskBarReceiver::runMenu(int, int)
+{
+ // do nothing
+}
+
+void
+OSXClientTaskBarReceiver::primaryAction()
+{
+ // do nothing
+}
+
+const IArchTaskBarReceiver::Icon
+OSXClientTaskBarReceiver::getIcon() const
+{
+ return NULL;
+}
+
+IArchTaskBarReceiver*
+createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events)
+{
+ return new OSXClientTaskBarReceiver(logBuffer, events);
+}
+
diff --git a/src/cmd/barrierc/OSXClientTaskBarReceiver.h b/src/cmd/barrierc/OSXClientTaskBarReceiver.h
new file mode 100644
index 0000000..fcc763a
--- /dev/null
+++ b/src/cmd/barrierc/OSXClientTaskBarReceiver.h
@@ -0,0 +1,37 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/ClientTaskBarReceiver.h"
+
+class BufferedLogOutputter;
+class IEventQueue;
+
+//! Implementation of ClientTaskBarReceiver for OS X
+class OSXClientTaskBarReceiver : public ClientTaskBarReceiver {
+public:
+ OSXClientTaskBarReceiver(const BufferedLogOutputter*, IEventQueue* events);
+ virtual ~OSXClientTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+};
diff --git a/src/cmd/barrierc/XWindowsClientTaskBarReceiver.cpp b/src/cmd/barrierc/XWindowsClientTaskBarReceiver.cpp
new file mode 100644
index 0000000..f56481c
--- /dev/null
+++ b/src/cmd/barrierc/XWindowsClientTaskBarReceiver.cpp
@@ -0,0 +1,68 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "XWindowsClientTaskBarReceiver.h"
+#include "arch/Arch.h"
+
+//
+// CXWindowsClientTaskBarReceiver
+//
+
+CXWindowsClientTaskBarReceiver::CXWindowsClientTaskBarReceiver(
+ const BufferedLogOutputter*,
+ IEventQueue* events) :
+ ClientTaskBarReceiver(events)
+{
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+CXWindowsClientTaskBarReceiver::~CXWindowsClientTaskBarReceiver()
+{
+ ARCH->removeReceiver(this);
+}
+
+void
+CXWindowsClientTaskBarReceiver::showStatus()
+{
+ // do nothing
+}
+
+void
+CXWindowsClientTaskBarReceiver::runMenu(int, int)
+{
+ // do nothing
+}
+
+void
+CXWindowsClientTaskBarReceiver::primaryAction()
+{
+ // do nothing
+}
+
+const IArchTaskBarReceiver::Icon
+CXWindowsClientTaskBarReceiver::getIcon() const
+{
+ return NULL;
+}
+
+IArchTaskBarReceiver*
+createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events)
+{
+ return new CXWindowsClientTaskBarReceiver(logBuffer, events);
+}
diff --git a/src/cmd/barrierc/XWindowsClientTaskBarReceiver.h b/src/cmd/barrierc/XWindowsClientTaskBarReceiver.h
new file mode 100644
index 0000000..73250d0
--- /dev/null
+++ b/src/cmd/barrierc/XWindowsClientTaskBarReceiver.h
@@ -0,0 +1,38 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/ClientTaskBarReceiver.h"
+
+class BufferedLogOutputter;
+class IEventQueue;
+
+//! Implementation of ClientTaskBarReceiver for X Windows
+class CXWindowsClientTaskBarReceiver : public ClientTaskBarReceiver {
+public:
+ CXWindowsClientTaskBarReceiver(
+ const BufferedLogOutputter*, IEventQueue* events);
+ virtual ~CXWindowsClientTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+};
diff --git a/src/cmd/barrierc/barrierc.cpp b/src/cmd/barrierc/barrierc.cpp
new file mode 100644
index 0000000..28d8efc
--- /dev/null
+++ b/src/cmd/barrierc/barrierc.cpp
@@ -0,0 +1,58 @@
+/*
+ * barrier -- 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 "barrier/ClientApp.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+#include "base/EventQueue.h"
+
+#if WINAPI_MSWINDOWS
+#include "MSWindowsClientTaskBarReceiver.h"
+#elif WINAPI_XWINDOWS
+#include "XWindowsClientTaskBarReceiver.h"
+#elif WINAPI_CARBON
+#include "OSXClientTaskBarReceiver.h"
+#else
+#error Platform not supported.
+#endif
+
+int
+main(int argc, char** argv)
+{
+#if SYSAPI_WIN32
+ // record window instance for tray icon, etc
+ ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
+#endif
+
+ Arch arch;
+ arch.init();
+
+ Log log;
+ EventQueue events;
+
+ ClientApp app(&events, createTaskBarReceiver);
+ int result = app.run(argc, argv);
+#if SYSAPI_WIN32
+ if (IsDebuggerPresent()) {
+ printf("\n\nHit a key to close...\n");
+ getchar();
+ }
+#endif
+ return result;
+
+}
diff --git a/src/cmd/barrierc/barrierc.ico b/src/cmd/barrierc/barrierc.ico
new file mode 100644
index 0000000..6e90545
--- /dev/null
+++ b/src/cmd/barrierc/barrierc.ico
Binary files differ
diff --git a/src/cmd/barrierc/barrierc.rc b/src/cmd/barrierc/barrierc.rc
new file mode 100644
index 0000000..b34127c
--- /dev/null
+++ b/src/cmd/barrierc/barrierc.rc
@@ -0,0 +1,141 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include <winresrc.h>
+#if !defined(IDC_STATIC)
+#define IDC_STATIC (-1)
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include <winresrc.h>\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_BARRIER ICON DISCARDABLE "barrierc.ico"
+IDI_TASKBAR_NOT_RUNNING ICON DISCARDABLE "tb_idle.ico"
+IDI_TASKBAR_NOT_WORKING ICON DISCARDABLE "tb_error.ico"
+IDI_TASKBAR_NOT_CONNECTED ICON DISCARDABLE "tb_wait.ico"
+IDI_TASKBAR_CONNECTED ICON DISCARDABLE "tb_run.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_TASKBAR_STATUS DIALOG DISCARDABLE 0, 0, 145, 18
+STYLE DS_MODALFRAME | WS_POPUP
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL |
+ ES_READONLY | NOT WS_BORDER
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_TASKBAR MENU DISCARDABLE
+BEGIN
+ POPUP "Barrier"
+ BEGIN
+ MENUITEM "Show Status", IDC_TASKBAR_STATUS
+ MENUITEM "Show Log", IDC_TASKBAR_SHOW_LOG
+ MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG
+ POPUP "Set Log Level"
+ BEGIN
+ MENUITEM "Error", IDC_TASKBAR_LOG_LEVEL_ERROR
+
+ MENUITEM "Warning", IDC_TASKBAR_LOG_LEVEL_WARNING
+
+ MENUITEM "Note", IDC_TASKBAR_LOG_LEVEL_NOTE
+
+ MENUITEM "Info", IDC_TASKBAR_LOG_LEVEL_INFO
+
+ MENUITEM "Debug", IDC_TASKBAR_LOG_LEVEL_DEBUG
+
+ MENUITEM "Debug1", IDC_TASKBAR_LOG_LEVEL_DEBUG1
+
+ MENUITEM "Debug2", IDC_TASKBAR_LOG_LEVEL_DEBUG2
+
+ END
+ MENUITEM SEPARATOR
+ MENUITEM "Quit", IDC_TASKBAR_QUIT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_FAILED "Barrier is about to quit with errors or warnings. Please check the log then click OK."
+ IDS_INIT_FAILED "Barrier failed to initialize: %{1}"
+ IDS_UNCAUGHT_EXCEPTION "Uncaught exception: %{1}"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/src/cmd/barrierc/resource.h b/src/cmd/barrierc/resource.h
new file mode 100644
index 0000000..57b271e
--- /dev/null
+++ b/src/cmd/barrierc/resource.h
@@ -0,0 +1,37 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by barrierc.rc
+//
+#define IDS_FAILED 1
+#define IDS_INIT_FAILED 2
+#define IDS_UNCAUGHT_EXCEPTION 3
+#define IDI_BARRIER 101
+#define IDI_TASKBAR_NOT_RUNNING 102
+#define IDI_TASKBAR_NOT_WORKING 103
+#define IDI_TASKBAR_NOT_CONNECTED 104
+#define IDI_TASKBAR_CONNECTED 105
+#define IDR_TASKBAR 107
+#define IDD_TASKBAR_STATUS 108
+#define IDC_TASKBAR_STATUS_STATUS 1000
+#define IDC_TASKBAR_QUIT 40001
+#define IDC_TASKBAR_STATUS 40002
+#define IDC_TASKBAR_LOG 40003
+#define IDC_TASKBAR_SHOW_LOG 40004
+#define IDC_TASKBAR_LOG_LEVEL_ERROR 40009
+#define IDC_TASKBAR_LOG_LEVEL_WARNING 40010
+#define IDC_TASKBAR_LOG_LEVEL_NOTE 40011
+#define IDC_TASKBAR_LOG_LEVEL_INFO 40012
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG 40013
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG1 40014
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG2 40015
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 109
+#define _APS_NEXT_COMMAND_VALUE 40016
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/src/cmd/barrierc/tb_error.ico b/src/cmd/barrierc/tb_error.ico
new file mode 100644
index 0000000..746a87c
--- /dev/null
+++ b/src/cmd/barrierc/tb_error.ico
Binary files differ
diff --git a/src/cmd/barrierc/tb_idle.ico b/src/cmd/barrierc/tb_idle.ico
new file mode 100644
index 0000000..4e13a26
--- /dev/null
+++ b/src/cmd/barrierc/tb_idle.ico
Binary files differ
diff --git a/src/cmd/barrierc/tb_run.ico b/src/cmd/barrierc/tb_run.ico
new file mode 100644
index 0000000..88e160c
--- /dev/null
+++ b/src/cmd/barrierc/tb_run.ico
Binary files differ
diff --git a/src/cmd/barrierc/tb_wait.ico b/src/cmd/barrierc/tb_wait.ico
new file mode 100644
index 0000000..257be0a
--- /dev/null
+++ b/src/cmd/barrierc/tb_wait.ico
Binary files differ
diff --git a/src/cmd/barrierd/CMakeLists.txt b/src/cmd/barrierd/CMakeLists.txt
new file mode 100644
index 0000000..aeae94c
--- /dev/null
+++ b/src/cmd/barrierd/CMakeLists.txt
@@ -0,0 +1,27 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2012 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (WIN32)
+ add_executable (barrierd WIN32 ${sources})
+else()
+ add_executable (barrierd ${sources})
+endif()
+
+target_link_libraries (barrierd
+ arch base common io ipc mt net platform synlib ${libs} ${OPENSSL_LIBS})
diff --git a/src/cmd/barrierd/barrierd.cpp b/src/cmd/barrierd/barrierd.cpp
new file mode 100644
index 0000000..dd351f9
--- /dev/null
+++ b/src/cmd/barrierd/barrierd.cpp
@@ -0,0 +1,44 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "barrier/win32/DaemonApp.h"
+
+#include <iostream>
+
+#ifdef SYSAPI_UNIX
+
+int
+main(int argc, char** argv)
+{
+ DaemonApp app;
+ return app.run(argc, argv);
+}
+
+#elif SYSAPI_WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+int WINAPI
+WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
+{
+ DaemonApp app;
+ return app.run(__argc, __argv);
+}
+
+#endif
diff --git a/src/cmd/barriers/.gitignore b/src/cmd/barriers/.gitignore
new file mode 100644
index 0000000..41a58c4
--- /dev/null
+++ b/src/cmd/barriers/.gitignore
@@ -0,0 +1 @@
+/*.aps
diff --git a/src/cmd/barriers/CMakeLists.txt b/src/cmd/barriers/CMakeLists.txt
new file mode 100644
index 0000000..e1871ee
--- /dev/null
+++ b/src/cmd/barriers/CMakeLists.txt
@@ -0,0 +1,58 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+set(sources
+ barriers.cpp
+)
+
+if (WIN32)
+ file(GLOB arch_headers "MSWindows*.h")
+ file(GLOB arch_sources "MSWindows*.cpp")
+ list(APPEND sources
+ resource.h
+ barriers.ico
+ barriers.rc
+ tb_error.ico
+ tb_idle.ico
+ tb_run.ico
+ tb_wait.ico
+ )
+elseif (APPLE)
+ file(GLOB arch_headers "OSX*.h")
+ file(GLOB arch_sources "OSX*.cpp")
+elseif (UNIX)
+ file(GLOB arch_headers "XWindows*.h")
+ file(GLOB arch_sources "XWindows*.cpp")
+endif()
+
+list(APPEND sources ${arch_sources})
+list(APPEND headers ${arch_headers})
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_executable(barriers ${sources})
+target_link_libraries(barriers
+ arch base client common io mt net ipc platform server synlib ${libs} ${OPENSSL_LIBS})
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ install (TARGETS barriers DESTINATION ${BARRIER_BUNDLE_BINARY_DIR})
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ install (TARGETS barriers DESTINATION bin)
+endif()
+
+
diff --git a/src/cmd/barriers/MSWindowsServerTaskBarReceiver.cpp b/src/cmd/barriers/MSWindowsServerTaskBarReceiver.cpp
new file mode 100644
index 0000000..a221dac
--- /dev/null
+++ b/src/cmd/barriers/MSWindowsServerTaskBarReceiver.cpp
@@ -0,0 +1,408 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "MSWindowsServerTaskBarReceiver.h"
+
+#include "resource.h"
+#include "server/Server.h"
+#include "platform/MSWindowsClipboard.h"
+#include "platform/MSWindowsScreen.h"
+#include "arch/win32/ArchTaskBarWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/Arch.h"
+#include "base/EventQueue.h"
+#include "base/IEventQueue.h"
+#include "base/log_outputters.h"
+#include "base/EventTypes.h"
+
+//
+// MSWindowsServerTaskBarReceiver
+//
+
+const UINT MSWindowsServerTaskBarReceiver::s_stateToIconID[kMaxState] =
+{
+ IDI_TASKBAR_NOT_RUNNING,
+ IDI_TASKBAR_NOT_WORKING,
+ IDI_TASKBAR_NOT_CONNECTED,
+ IDI_TASKBAR_CONNECTED
+};
+
+MSWindowsServerTaskBarReceiver::MSWindowsServerTaskBarReceiver(
+ HINSTANCE appInstance, const BufferedLogOutputter* logBuffer, IEventQueue* events) :
+ ServerTaskBarReceiver(events),
+ m_events(events),
+ m_appInstance(appInstance),
+ m_window(NULL),
+ m_logBuffer(logBuffer)
+{
+ for (UInt32 i = 0; i < kMaxState; ++i) {
+ m_icon[i] = loadIcon(s_stateToIconID[i]);
+ }
+ m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR));
+
+ // don't create the window yet. we'll create it on demand. this
+ // has the side benefit of being created in the thread used for
+ // the task bar. that's good because it means the existence of
+ // the window won't prevent changing the main thread's desktop.
+
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+void
+MSWindowsServerTaskBarReceiver::cleanup()
+{
+ ARCH->removeReceiver(this);
+ for (UInt32 i = 0; i < kMaxState; ++i) {
+ deleteIcon(m_icon[i]);
+ }
+ DestroyMenu(m_menu);
+ destroyWindow();
+}
+
+MSWindowsServerTaskBarReceiver::~MSWindowsServerTaskBarReceiver()
+{
+ cleanup();
+}
+
+void
+MSWindowsServerTaskBarReceiver::showStatus()
+{
+ // create the window
+ createWindow();
+
+ // lock self while getting status
+ lock();
+
+ // get the current status
+ std::string status = getToolTip();
+
+ // get the connect clients, if any
+ const Clients& clients = getClients();
+
+ // done getting status
+ unlock();
+
+ // update dialog
+ HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS);
+ SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str());
+ child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS);
+ SendMessage(child, LB_RESETCONTENT, 0, 0);
+ for (Clients::const_iterator index = clients.begin();
+ index != clients.end(); ) {
+ const char* client = index->c_str();
+ if (++index == clients.end()) {
+ SendMessage(child, LB_ADDSTRING, 0, (LPARAM)client);
+ }
+ else {
+ SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)client);
+ }
+ }
+
+ if (!IsWindowVisible(m_window)) {
+ // position it by the mouse
+ POINT cursorPos;
+ GetCursorPos(&cursorPos);
+ RECT windowRect;
+ GetWindowRect(m_window, &windowRect);
+ int x = cursorPos.x;
+ int y = cursorPos.y;
+ int fw = GetSystemMetrics(SM_CXDLGFRAME);
+ int fh = GetSystemMetrics(SM_CYDLGFRAME);
+ int ww = windowRect.right - windowRect.left;
+ int wh = windowRect.bottom - windowRect.top;
+ int sw = GetSystemMetrics(SM_CXFULLSCREEN);
+ int sh = GetSystemMetrics(SM_CYFULLSCREEN);
+ if (fw < 1) {
+ fw = 1;
+ }
+ if (fh < 1) {
+ fh = 1;
+ }
+ if (x + ww - fw > sw) {
+ x -= ww - fw;
+ }
+ else {
+ x -= fw;
+ }
+ if (x < 0) {
+ x = 0;
+ }
+ if (y + wh - fh > sh) {
+ y -= wh - fh;
+ }
+ else {
+ y -= fh;
+ }
+ if (y < 0) {
+ y = 0;
+ }
+ SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh,
+ SWP_SHOWWINDOW);
+ }
+}
+
+void
+MSWindowsServerTaskBarReceiver::runMenu(int x, int y)
+{
+ // do popup menu. we need a window to pass to TrackPopupMenu().
+ // the SetForegroundWindow() and SendMessage() calls around
+ // TrackPopupMenu() are to get the menu to be dismissed when
+ // another window gets activated and are just one of those
+ // win32 weirdnesses.
+ createWindow();
+ SetForegroundWindow(m_window);
+ HMENU menu = GetSubMenu(m_menu, 0);
+ SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE);
+ HMENU logLevelMenu = GetSubMenu(menu, 3);
+ CheckMenuRadioItem(logLevelMenu, 0, 6,
+ CLOG->getFilter() - kERROR, MF_BYPOSITION);
+ int n = TrackPopupMenu(menu,
+ TPM_NONOTIFY |
+ TPM_RETURNCMD |
+ TPM_LEFTBUTTON |
+ TPM_RIGHTBUTTON,
+ x, y, 0, m_window, NULL);
+ SendMessage(m_window, WM_NULL, 0, 0);
+
+ // perform the requested operation
+ switch (n) {
+ case IDC_TASKBAR_STATUS:
+ showStatus();
+ break;
+
+ case IDC_TASKBAR_LOG:
+ copyLog();
+ break;
+
+ case IDC_TASKBAR_SHOW_LOG:
+ ARCH->showConsole(true);
+ break;
+
+ case IDC_RELOAD_CONFIG:
+ m_events->addEvent(Event(m_events->forServerApp().reloadConfig(),
+ m_events->getSystemTarget()));
+ break;
+
+ case IDC_FORCE_RECONNECT:
+ m_events->addEvent(Event(m_events->forServerApp().forceReconnect(),
+ m_events->getSystemTarget()));
+ break;
+
+ case ID_BARRIER_RESETSERVER:
+ m_events->addEvent(Event(m_events->forServerApp().resetServer(),
+ m_events->getSystemTarget()));
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_ERROR:
+ CLOG->setFilter(kERROR);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_WARNING:
+ CLOG->setFilter(kWARNING);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_NOTE:
+ CLOG->setFilter(kNOTE);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_INFO:
+ CLOG->setFilter(kINFO);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG:
+ CLOG->setFilter(kDEBUG);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG1:
+ CLOG->setFilter(kDEBUG1);
+ break;
+
+ case IDC_TASKBAR_LOG_LEVEL_DEBUG2:
+ CLOG->setFilter(kDEBUG2);
+ break;
+
+ case IDC_TASKBAR_QUIT:
+ quit();
+ break;
+ }
+}
+
+void
+MSWindowsServerTaskBarReceiver::primaryAction()
+{
+ showStatus();
+}
+
+const IArchTaskBarReceiver::Icon
+MSWindowsServerTaskBarReceiver::getIcon() const
+{
+ return static_cast<Icon>(m_icon[getStatus()]);
+}
+
+void
+MSWindowsServerTaskBarReceiver::copyLog() const
+{
+ if (m_logBuffer != NULL) {
+ // collect log buffer
+ String data;
+ for (BufferedLogOutputter::const_iterator index = m_logBuffer->begin();
+ index != m_logBuffer->end(); ++index) {
+ data += *index;
+ data += "\n";
+ }
+
+ // copy log to clipboard
+ if (!data.empty()) {
+ MSWindowsClipboard clipboard(m_window);
+ clipboard.open(0);
+ clipboard.emptyUnowned();
+ clipboard.add(IClipboard::kText, data);
+ clipboard.close();
+ }
+ }
+}
+
+void
+MSWindowsServerTaskBarReceiver::onStatusChanged()
+{
+ if (IsWindowVisible(m_window)) {
+ showStatus();
+ }
+}
+
+HICON
+MSWindowsServerTaskBarReceiver::loadIcon(UINT id)
+{
+ HANDLE icon = LoadImage(m_appInstance,
+ MAKEINTRESOURCE(id),
+ IMAGE_ICON,
+ 0, 0,
+ LR_DEFAULTCOLOR);
+ return static_cast<HICON>(icon);
+}
+
+void
+MSWindowsServerTaskBarReceiver::deleteIcon(HICON icon)
+{
+ if (icon != NULL) {
+ DestroyIcon(icon);
+ }
+}
+
+void
+MSWindowsServerTaskBarReceiver::createWindow()
+{
+ // ignore if already created
+ if (m_window != NULL) {
+ return;
+ }
+
+ // get the status dialog
+ m_window = CreateDialogParam(m_appInstance,
+ MAKEINTRESOURCE(IDD_TASKBAR_STATUS),
+ NULL,
+ (DLGPROC)&MSWindowsServerTaskBarReceiver::staticDlgProc,
+ reinterpret_cast<LPARAM>(
+ static_cast<void*>(this)));
+
+ // window should appear on top of everything, including (especially)
+ // the task bar.
+ LONG_PTR style = GetWindowLongPtr(m_window, GWL_EXSTYLE);
+ style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
+ SetWindowLongPtr(m_window, GWL_EXSTYLE, style);
+
+ // tell the task bar about this dialog
+ ArchTaskBarWindows::addDialog(m_window);
+}
+
+void
+MSWindowsServerTaskBarReceiver::destroyWindow()
+{
+ if (m_window != NULL) {
+ ArchTaskBarWindows::removeDialog(m_window);
+ DestroyWindow(m_window);
+ m_window = NULL;
+ }
+}
+
+BOOL
+MSWindowsServerTaskBarReceiver::dlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ // use default focus
+ return TRUE;
+
+ case WM_ACTIVATE:
+ // hide when another window is activated
+ if (LOWORD(wParam) == WA_INACTIVE) {
+ ShowWindow(hwnd, SW_HIDE);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+BOOL CALLBACK
+MSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ // if msg is WM_INITDIALOG, extract the MSWindowsServerTaskBarReceiver*
+ // and put it in the extra window data then forward the call.
+ MSWindowsServerTaskBarReceiver* self = NULL;
+ if (msg == WM_INITDIALOG) {
+ self = static_cast<MSWindowsServerTaskBarReceiver*>(
+ reinterpret_cast<void*>(lParam));
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
+ }
+ else {
+ // get the extra window data and forward the call
+ LONG_PTR data = GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ if (data != 0) {
+ self = static_cast<MSWindowsServerTaskBarReceiver*>(
+ reinterpret_cast<void*>(data));
+ }
+ }
+
+ // forward the message
+ if (self != NULL) {
+ return self->dlgProc(hwnd, msg, wParam, lParam);
+ }
+ else {
+ return (msg == WM_INITDIALOG) ? TRUE : FALSE;
+ }
+}
+
+IArchTaskBarReceiver*
+createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events)
+{
+ ArchMiscWindows::setIcons(
+ (HICON)LoadImage(ArchMiscWindows::instanceWin32(),
+ MAKEINTRESOURCE(IDI_BARRIER),
+ IMAGE_ICON,
+ 32, 32, LR_SHARED),
+ (HICON)LoadImage(ArchMiscWindows::instanceWin32(),
+ MAKEINTRESOURCE(IDI_BARRIER),
+ IMAGE_ICON,
+ 16, 16, LR_SHARED));
+
+ return new MSWindowsServerTaskBarReceiver(
+ MSWindowsScreen::getWindowInstance(), logBuffer, events);
+}
diff --git a/src/cmd/barriers/MSWindowsServerTaskBarReceiver.h b/src/cmd/barriers/MSWindowsServerTaskBarReceiver.h
new file mode 100644
index 0000000..a308ab4
--- /dev/null
+++ b/src/cmd/barriers/MSWindowsServerTaskBarReceiver.h
@@ -0,0 +1,69 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/ServerTaskBarReceiver.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+class BufferedLogOutputter;
+class IEventQueue;
+
+//! Implementation of ServerTaskBarReceiver for Microsoft Windows
+class MSWindowsServerTaskBarReceiver : public ServerTaskBarReceiver {
+public:
+ MSWindowsServerTaskBarReceiver(HINSTANCE, const BufferedLogOutputter*, IEventQueue* events);
+ virtual ~MSWindowsServerTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+ void cleanup();
+
+protected:
+ void copyLog() const;
+
+ // ServerTaskBarReceiver overrides
+ virtual void onStatusChanged();
+
+private:
+ HICON loadIcon(UINT);
+ void deleteIcon(HICON);
+ void createWindow();
+ void destroyWindow();
+
+ BOOL dlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam);
+ static BOOL CALLBACK
+ staticDlgProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam);
+
+private:
+ HINSTANCE m_appInstance;
+ HWND m_window;
+ HMENU m_menu;
+ HICON m_icon[kMaxState];
+ const BufferedLogOutputter* m_logBuffer;
+ IEventQueue* m_events;
+
+ static const UINT s_stateToIconID[];
+};
diff --git a/src/cmd/barriers/OSXServerTaskBarReceiver.cpp b/src/cmd/barriers/OSXServerTaskBarReceiver.cpp
new file mode 100644
index 0000000..bbe8fd1
--- /dev/null
+++ b/src/cmd/barriers/OSXServerTaskBarReceiver.cpp
@@ -0,0 +1,67 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "OSXServerTaskBarReceiver.h"
+#include "arch/Arch.h"
+
+//
+// OSXServerTaskBarReceiver
+//
+
+OSXServerTaskBarReceiver::OSXServerTaskBarReceiver(
+ const BufferedLogOutputter*, IEventQueue* events) :
+ ServerTaskBarReceiver(events)
+{
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+OSXServerTaskBarReceiver::~OSXServerTaskBarReceiver()
+{
+ ARCH->removeReceiver(this);
+}
+
+void
+OSXServerTaskBarReceiver::showStatus()
+{
+ // do nothing
+}
+
+void
+OSXServerTaskBarReceiver::runMenu(int, int)
+{
+ // do nothing
+}
+
+void
+OSXServerTaskBarReceiver::primaryAction()
+{
+ // do nothing
+}
+
+const IArchTaskBarReceiver::Icon
+OSXServerTaskBarReceiver::getIcon() const
+{
+ return NULL;
+}
+
+IArchTaskBarReceiver*
+createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events)
+{
+ return new OSXServerTaskBarReceiver(logBuffer, events);
+}
diff --git a/src/cmd/barriers/OSXServerTaskBarReceiver.h b/src/cmd/barriers/OSXServerTaskBarReceiver.h
new file mode 100644
index 0000000..ab6928f
--- /dev/null
+++ b/src/cmd/barriers/OSXServerTaskBarReceiver.h
@@ -0,0 +1,36 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/ServerTaskBarReceiver.h"
+
+class BufferedLogOutputter;
+
+//! Implementation of ServerTaskBarReceiver for OS X
+class OSXServerTaskBarReceiver : public ServerTaskBarReceiver {
+public:
+ OSXServerTaskBarReceiver(const BufferedLogOutputter*, IEventQueue* events);
+ virtual ~OSXServerTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+};
diff --git a/src/cmd/barriers/XWindowsServerTaskBarReceiver.cpp b/src/cmd/barriers/XWindowsServerTaskBarReceiver.cpp
new file mode 100644
index 0000000..7f525c5
--- /dev/null
+++ b/src/cmd/barriers/XWindowsServerTaskBarReceiver.cpp
@@ -0,0 +1,67 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "XWindowsServerTaskBarReceiver.h"
+#include "arch/Arch.h"
+
+//
+// CXWindowsServerTaskBarReceiver
+//
+
+CXWindowsServerTaskBarReceiver::CXWindowsServerTaskBarReceiver(
+ const BufferedLogOutputter*, IEventQueue* events) :
+ ServerTaskBarReceiver(events)
+{
+ // add ourself to the task bar
+ ARCH->addReceiver(this);
+}
+
+CXWindowsServerTaskBarReceiver::~CXWindowsServerTaskBarReceiver()
+{
+ ARCH->removeReceiver(this);
+}
+
+void
+CXWindowsServerTaskBarReceiver::showStatus()
+{
+ // do nothing
+}
+
+void
+CXWindowsServerTaskBarReceiver::runMenu(int, int)
+{
+ // do nothing
+}
+
+void
+CXWindowsServerTaskBarReceiver::primaryAction()
+{
+ // do nothing
+}
+
+const IArchTaskBarReceiver::Icon
+CXWindowsServerTaskBarReceiver::getIcon() const
+{
+ return NULL;
+}
+
+IArchTaskBarReceiver*
+createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events)
+{
+ return new CXWindowsServerTaskBarReceiver(logBuffer, events);
+}
diff --git a/src/cmd/barriers/XWindowsServerTaskBarReceiver.h b/src/cmd/barriers/XWindowsServerTaskBarReceiver.h
new file mode 100644
index 0000000..549a62f
--- /dev/null
+++ b/src/cmd/barriers/XWindowsServerTaskBarReceiver.h
@@ -0,0 +1,38 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/ServerTaskBarReceiver.h"
+
+class BufferedLogOutputter;
+class IEventQueue;
+
+//! Implementation of ServerTaskBarReceiver for X Windows
+class CXWindowsServerTaskBarReceiver : public ServerTaskBarReceiver {
+public:
+ CXWindowsServerTaskBarReceiver(
+ const BufferedLogOutputter*, IEventQueue* events);
+ virtual ~CXWindowsServerTaskBarReceiver();
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus();
+ virtual void runMenu(int x, int y);
+ virtual void primaryAction();
+ virtual const Icon getIcon() const;
+};
diff --git a/src/cmd/barriers/barriers.cpp b/src/cmd/barriers/barriers.cpp
new file mode 100644
index 0000000..cd67bcb
--- /dev/null
+++ b/src/cmd/barriers/barriers.cpp
@@ -0,0 +1,57 @@
+/*
+ * barrier -- 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 "barrier/ServerApp.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+#include "base/EventQueue.h"
+
+#if WINAPI_MSWINDOWS
+#include "MSWindowsServerTaskBarReceiver.h"
+#elif WINAPI_XWINDOWS
+#include "XWindowsServerTaskBarReceiver.h"
+#elif WINAPI_CARBON
+#include "OSXServerTaskBarReceiver.h"
+#else
+#error Platform not supported.
+#endif
+
+int
+main(int argc, char** argv)
+{
+#if SYSAPI_WIN32
+ // record window instance for tray icon, etc
+ ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
+#endif
+
+ Arch arch;
+ arch.init();
+
+ Log log;
+ EventQueue events;
+
+ ServerApp app(&events, createTaskBarReceiver);
+ int result = app.run(argc, argv);
+#if SYSAPI_WIN32
+ if (IsDebuggerPresent()) {
+ printf("\n\nHit a key to close...\n");
+ getchar();
+ }
+#endif
+ return result;
+}
diff --git a/src/cmd/barriers/barriers.ico b/src/cmd/barriers/barriers.ico
new file mode 100644
index 0000000..6e90545
--- /dev/null
+++ b/src/cmd/barriers/barriers.ico
Binary files differ
diff --git a/src/cmd/barriers/barriers.rc b/src/cmd/barriers/barriers.rc
new file mode 100644
index 0000000..c4d263c
--- /dev/null
+++ b/src/cmd/barriers/barriers.rc
@@ -0,0 +1,134 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include <winresrc.h>
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include <winresrc.h>\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_BARRIER ICON "barriers.ico"
+IDI_TASKBAR_NOT_RUNNING ICON "tb_idle.ico"
+IDI_TASKBAR_NOT_WORKING ICON "tb_error.ico"
+IDI_TASKBAR_NOT_CONNECTED ICON "tb_wait.ico"
+IDI_TASKBAR_CONNECTED ICON "tb_run.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_TASKBAR MENU
+BEGIN
+ POPUP "Barrier"
+ BEGIN
+ MENUITEM "Show Status", IDC_TASKBAR_STATUS
+ MENUITEM "Show Log", IDC_TASKBAR_SHOW_LOG
+ MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG
+ POPUP "Set Log Level"
+ BEGIN
+ MENUITEM "Error", IDC_TASKBAR_LOG_LEVEL_ERROR
+ MENUITEM "Warning", IDC_TASKBAR_LOG_LEVEL_WARNING
+ MENUITEM "Note", IDC_TASKBAR_LOG_LEVEL_NOTE
+ MENUITEM "Info", IDC_TASKBAR_LOG_LEVEL_INFO
+ MENUITEM "Debug", IDC_TASKBAR_LOG_LEVEL_DEBUG
+ MENUITEM "Debug1", IDC_TASKBAR_LOG_LEVEL_DEBUG1
+ MENUITEM "Debug2", IDC_TASKBAR_LOG_LEVEL_DEBUG2
+ END
+ MENUITEM "Reload Configuration", IDC_RELOAD_CONFIG
+ MENUITEM "Force Reconnect", IDC_FORCE_RECONNECT
+ MENUITEM "Reset Server", ID_BARRIER_RESETSERVER
+ MENUITEM SEPARATOR
+ MENUITEM "Quit", IDC_TASKBAR_QUIT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_TASKBAR_STATUS DIALOG 0, 0, 145, 60
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP
+FONT 8, "MS Sans Serif"
+BEGIN
+ EDITTEXT IDC_TASKBAR_STATUS_STATUS,3,3,139,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER
+ LISTBOX IDC_TASKBAR_STATUS_CLIENTS,3,17,139,40,NOT LBS_NOTIFY | LBS_SORT | LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_VSCROLL | WS_TABSTOP
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_FAILED "Barrier is about to quit with errors or warnings. Please check the log then click OK."
+ IDS_INIT_FAILED "Barrier failed to initialize: %{1}"
+ IDS_UNCAUGHT_EXCEPTION "Uncaught exception: %{1}"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/src/cmd/barriers/resource.h b/src/cmd/barriers/resource.h
new file mode 100644
index 0000000..9594db1
--- /dev/null
+++ b/src/cmd/barriers/resource.h
@@ -0,0 +1,42 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by barriers.rc
+//
+#define IDS_FAILED 1
+#define IDS_INIT_FAILED 2
+#define IDS_UNCAUGHT_EXCEPTION 3
+#define IDI_BARRIER 101
+#define IDI_TASKBAR_NOT_RUNNING 102
+#define IDI_TASKBAR_NOT_WORKING 103
+#define IDI_TASKBAR_NOT_CONNECTED 104
+#define IDI_TASKBAR_CONNECTED 105
+#define IDR_TASKBAR 107
+#define IDD_TASKBAR_STATUS 108
+#define IDC_TASKBAR_STATUS_STATUS 1000
+#define IDC_TASKBAR_STATUS_CLIENTS 1001
+#define IDC_TASKBAR_QUIT 40003
+#define IDC_TASKBAR_STATUS 40004
+#define IDC_TASKBAR_LOG 40005
+#define IDC_RELOAD_CONFIG 40006
+#define IDC_FORCE_RECONNECT 40007
+#define IDC_TASKBAR_SHOW_LOG 40008
+#define IDC_TASKBAR_LOG_LEVEL_ERROR 40009
+#define IDC_TASKBAR_LOG_LEVEL_WARNING 40010
+#define IDC_TASKBAR_LOG_LEVEL_NOTE 40011
+#define IDC_TASKBAR_LOG_LEVEL_INFO 40012
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG 40013
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG1 40014
+#define IDC_TASKBAR_LOG_LEVEL_DEBUG2 40015
+#define ID_BARRIER_RELOADSYSTEM 40016
+#define ID_BARRIER_RESETSERVER 40017
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 109
+#define _APS_NEXT_COMMAND_VALUE 40018
+#define _APS_NEXT_CONTROL_VALUE 1003
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/src/cmd/barriers/tb_error.ico b/src/cmd/barriers/tb_error.ico
new file mode 100644
index 0000000..746a87c
--- /dev/null
+++ b/src/cmd/barriers/tb_error.ico
Binary files differ
diff --git a/src/cmd/barriers/tb_idle.ico b/src/cmd/barriers/tb_idle.ico
new file mode 100644
index 0000000..4e13a26
--- /dev/null
+++ b/src/cmd/barriers/tb_idle.ico
Binary files differ
diff --git a/src/cmd/barriers/tb_run.ico b/src/cmd/barriers/tb_run.ico
new file mode 100644
index 0000000..88e160c
--- /dev/null
+++ b/src/cmd/barriers/tb_run.ico
Binary files differ
diff --git a/src/cmd/barriers/tb_wait.ico b/src/cmd/barriers/tb_wait.ico
new file mode 100644
index 0000000..257be0a
--- /dev/null
+++ b/src/cmd/barriers/tb_wait.ico
Binary files differ
diff --git a/src/cmd/syntool/CMakeLists.txt b/src/cmd/syntool/CMakeLists.txt
new file mode 100644
index 0000000..863d4f4
--- /dev/null
+++ b/src/cmd/syntool/CMakeLists.txt
@@ -0,0 +1,27 @@
+# barrier -- 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+add_executable(syntool ${sources})
+target_link_libraries(syntool
+ synlib arch base client common io ipc mt net platform server ${libs} ${OPENSSL_LIBS})
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ install (TARGETS syntool DESTINATION ${BARRIER_BUNDLE_BINARY_DIR})
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ install (TARGETS syntool DESTINATION bin)
+endif()
diff --git a/src/cmd/syntool/syntool.cpp b/src/cmd/syntool/syntool.cpp
new file mode 100644
index 0000000..72d35b9
--- /dev/null
+++ b/src/cmd/syntool/syntool.cpp
@@ -0,0 +1,31 @@
+/*
+ * barrier -- 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 "barrier/ToolApp.h"
+#include "arch/Arch.h"
+
+int
+main(int argc, char** argv)
+{
+#if SYSAPI_WIN32
+ // record window instance for tray icon, etc
+ ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
+#endif
+
+ ToolApp app;
+ return app.run(argc, argv);
+}
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
new file mode 100644
index 0000000..5d24891
--- /dev/null
+++ b/src/gui/CMakeLists.txt
@@ -0,0 +1,52 @@
+cmake_minimum_required (VERSION 3.4)
+
+find_package (Qt5 COMPONENTS Core Widgets Network)
+set (CMAKE_AUTOMOC ON)
+set (CMAKE_AUTORCC ON)
+set (CMAKE_AUTOUIC ON)
+set (CMAKE_INCLUDE_CURRENT_DIR ON)
+
+file (GLOB GUI_SOURCE_FILES src/*.cpp src/*.h)
+file (GLOB GUI_UI_FILES src/*.ui)
+
+if (WIN32)
+ set (GUI_RC_FILES res/win/Barrier.rc)
+endif()
+
+add_executable (barrier WIN32
+ ${GUI_SOURCE_FILES}
+ ${GUI_UI_FILES}
+ ${GUI_RC_FILES}
+ res/Barrier.qrc
+)
+
+include_directories (./src)
+
+qt5_use_modules (barrier Core Widgets Network)
+target_compile_definitions (barrier PRIVATE -DBARRIER_VERSION_STAGE="${BARRIER_VERSION_STAGE}")
+target_compile_definitions (barrier PRIVATE -DBARRIER_REVISION="${BARRIER_REVISION}")
+
+if (WIN32)
+ include_directories ($ENV{BONJOUR_SDK_HOME}/Include)
+ find_library (DNSSD_LIB dnssd.lib
+ HINTS ENV BONJOUR_SDK_HOME
+ PATH_SUFFIXES "Lib/x64")
+ set_target_properties (barrier PROPERTIES LINK_FLAGS "/NODEFAULTLIB:LIBCMT")
+ target_link_libraries (barrier ${DNSSD_LIB})
+elseif (APPLE)
+ find_library(APPSERVICES_LIB ApplicationServices)
+ target_link_libraries(barrier ${APPSERVICES_LIB})
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR
+ ${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
+ target_link_libraries (barrier dns_sd)
+endif()
+
+if (HAVE_X11)
+ target_link_libraries (barrier X11)
+endif()
+
+if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+ install (TARGETS barrier DESTINATION ${BARRIER_BUNDLE_BINARY_DIR})
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ install (TARGETS barrier DESTINATION bin)
+endif()
diff --git a/src/gui/gui.pro b/src/gui/gui.pro
new file mode 100644
index 0000000..14a2407
--- /dev/null
+++ b/src/gui/gui.pro
@@ -0,0 +1,162 @@
+QT += widgets \
+ network
+TEMPLATE = app
+TARGET = barrier
+DEFINES += VERSION_STAGE=\\\"$$QMAKE_VERSION_STAGE\\\"
+DEFINES += VERSION_REVISION=\\\"$$QMAKE_VERSION_REVISION\\\"
+DEFINES -= UNICODE
+DEFINES += _MBCS
+DEPENDPATH += . \
+ res
+INCLUDEPATH += . \
+ src \
+ ../lib/shared/
+FORMS += src/MainWindowBase.ui \
+ src/AboutDialogBase.ui \
+ src/ServerConfigDialogBase.ui \
+ src/ScreenSettingsDialogBase.ui \
+ src/ActionDialogBase.ui \
+ src/HotkeyDialogBase.ui \
+ src/SettingsDialogBase.ui \
+ src/SetupWizardBase.ui \
+ src/AddClientDialogBase.ui \
+ src/ActivationDialog.ui \
+ src/CancelActivationDialog.ui \
+ src/FailedLoginDialog.ui
+SOURCES += src/main.cpp \
+ src/MainWindow.cpp \
+ src/AboutDialog.cpp \
+ src/ServerConfig.cpp \
+ src/ServerConfigDialog.cpp \
+ src/ScreenSetupView.cpp \
+ src/Screen.cpp \
+ src/ScreenSetupModel.cpp \
+ src/NewScreenWidget.cpp \
+ src/TrashScreenWidget.cpp \
+ src/ScreenSettingsDialog.cpp \
+ src/BaseConfig.cpp \
+ src/HotkeyDialog.cpp \
+ src/ActionDialog.cpp \
+ src/Hotkey.cpp \
+ src/Action.cpp \
+ src/KeySequence.cpp \
+ src/KeySequenceWidget.cpp \
+ src/SettingsDialog.cpp \
+ src/AppConfig.cpp \
+ src/QBarrierApplication.cpp \
+ src/VersionChecker.cpp \
+ src/SetupWizard.cpp \
+ src/IpcClient.cpp \
+ src/IpcReader.cpp \
+ src/Ipc.cpp \
+ src/BarrierLocale.cpp \
+ src/QUtility.cpp \
+ src/ZeroconfServer.cpp \
+ src/ZeroconfThread.cpp \
+ src/ZeroconfRegister.cpp \
+ src/ZeroconfBrowser.cpp \
+ src/ZeroconfService.cpp \
+ src/DataDownloader.cpp \
+ src/AddClientDialog.cpp \
+ src/CommandProcess.cpp \
+ src/CoreInterface.cpp \
+ src/Fingerprint.cpp \
+ src/SslCertificate.cpp \
+ src/WebClient.cpp \
+ src/ActivationNotifier.cpp \
+ src/ActivationDialog.cpp \
+ src/CancelActivationDialog.cpp \
+ src/FailedLoginDialog.cpp \
+ ../lib/shared/SerialKey.cpp \
+ src/LicenseManager.cpp
+HEADERS += src/MainWindow.h \
+ src/AboutDialog.h \
+ src/ServerConfig.h \
+ src/ServerConfigDialog.h \
+ src/ScreenSetupView.h \
+ src/Screen.h \
+ src/ScreenSetupModel.h \
+ src/NewScreenWidget.h \
+ src/TrashScreenWidget.h \
+ src/ScreenSettingsDialog.h \
+ src/BaseConfig.h \
+ src/HotkeyDialog.h \
+ src/ActionDialog.h \
+ src/Hotkey.h \
+ src/Action.h \
+ src/KeySequence.h \
+ src/KeySequenceWidget.h \
+ src/SettingsDialog.h \
+ src/AppConfig.h \
+ src/QBarrierApplication.h \
+ src/VersionChecker.h \
+ src/SetupWizard.h \
+ src/IpcClient.h \
+ src/IpcReader.h \
+ src/Ipc.h \
+ src/BarrierLocale.h \
+ src/QUtility.h \
+ src/ZeroconfServer.h \
+ src/ZeroconfThread.h \
+ src/ZeroconfRegister.h \
+ src/ZeroconfRecord.h \
+ src/ZeroconfBrowser.h \
+ src/ZeroconfService.h \
+ src/DataDownloader.h \
+ src/AddClientDialog.h \
+ src/CommandProcess.h \
+ src/ProcessorArch.h \
+ src/CoreInterface.h \
+ src/Fingerprint.h \
+ src/SslCertificate.h \
+ src/WebClient.h \
+ src/ActivationNotifier.h \
+ src/ElevateMode.h \
+ src/ActivationDialog.h \
+ src/CancelActivationDialog.h \
+ src/FailedLoginDialog.h \
+ ../lib/shared/EditionType.h \
+ ../lib/shared/SerialKey.h \
+ src/LicenseManager.h
+RESOURCES += res/Barrier.qrc
+RC_FILE = res/win/Barrier.rc
+macx {
+ QMAKE_INFO_PLIST = res/mac/Info.plist
+ TARGET = Barrier
+ QBARRIER_ICON.files = res/mac/Barrier.icns
+ QBARRIER_ICON.path = Contents/Resources
+ QMAKE_BUNDLE_DATA += QBARRIER_ICON
+ LIBS += $$MACX_LIBS
+}
+unix:!macx:LIBS += -ldns_sd
+debug {
+ OBJECTS_DIR = tmp/debug
+ MOC_DIR = tmp/debug
+ RCC_DIR = tmp/debug
+}
+release {
+ OBJECTS_DIR = tmp/release
+ MOC_DIR = tmp/release
+ RCC_DIR = tmp/release
+}
+win32-msvc2015 {
+ LIBS += -lAdvapi32
+ QMAKE_LFLAGS += /NODEFAULTLIB:LIBCMT
+}
+win32-msvc* {
+ contains(QMAKE_HOST.arch, x86):{
+ QMAKE_LFLAGS *= /MACHINE:X86
+ LIBS += -L"$$(BONJOUR_SDK_HOME)/Lib/Win32" -ldnssd
+ }
+
+ contains(QMAKE_HOST.arch, x86_64):{
+ QMAKE_LFLAGS *= /MACHINE:X64
+ LIBS += -L"$$(BONJOUR_SDK_HOME)/Lib/x64" -ldnssd
+ }
+}
+win32 {
+ Debug:DESTDIR = ../../bin/Debug
+ Release:DESTDIR = ../../bin/Release
+ INCLUDEPATH += "$$(BONJOUR_SDK_HOME)/Include"
+}
+else:DESTDIR = ../../bin
diff --git a/src/gui/gui.ts b/src/gui/gui.ts
new file mode 100644
index 0000000..861a496
--- /dev/null
+++ b/src/gui/gui.ts
@@ -0,0 +1,1407 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="en" sourcelanguage="en">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2018 Debauchee Open Source Group&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2018 Debauchee Open Source Group&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server&apos;s screen. If the two don&apos;t match exactly, then it&apos;s probably not the server you&apos;re expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer&apos;s mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer&apos;s mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin &apos;%1&apos; to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading &apos;%1&apos; plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don&apos;t take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it&apos;s Free and Open Source. Just move your mouse off the edge of one computer&apos;s screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer&apos;s mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer&apos;s screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer&apos;s mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server&apos;s mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS>
diff --git a/src/gui/lang.cmd b/src/gui/lang.cmd
new file mode 100644
index 0000000..4418593
--- /dev/null
+++ b/src/gui/lang.cmd
@@ -0,0 +1 @@
+lupdate -noobsolete gui.pro -ts gui.ts \ No newline at end of file
diff --git a/src/gui/langbuild.cmd b/src/gui/langbuild.cmd
new file mode 100644
index 0000000..b86d202
--- /dev/null
+++ b/src/gui/langbuild.cmd
@@ -0,0 +1,2 @@
+cd res/lang
+lrelease *.ts \ No newline at end of file
diff --git a/src/gui/res/Barrier.qrc b/src/gui/res/Barrier.qrc
new file mode 100644
index 0000000..39da79e
--- /dev/null
+++ b/src/gui/res/Barrier.qrc
@@ -0,0 +1,58 @@
+<RCC>
+ <qresource prefix="/res">
+ <file>icons/16x16/barrier-connected.png</file>
+ <file>icons/16x16/barrier-disconnected.png</file>
+ <file>icons/64x64/video-display.png</file>
+ <file>icons/64x64/user-trash.png</file>
+ <file>icons/16x16/warning.png</file>
+ <file>icons/256x256/barrier.ico</file>
+ <file>image/about.png</file>
+ <file>lang/gui_ar.qm</file>
+ <file>lang/gui_bg-BG.qm</file>
+ <file>lang/gui_ca-AD.qm</file>
+ <file>lang/gui_cs-CZ.qm</file>
+ <file>lang/gui_cy.qm</file>
+ <file>lang/gui_da.qm</file>
+ <file>lang/gui_de.qm</file>
+ <file>lang/gui_es.qm</file>
+ <file>lang/gui_fi.qm</file>
+ <file>lang/gui_fr.qm</file>
+ <file>lang/gui_grk.qm</file>
+ <file>lang/gui_he.qm</file>
+ <file>lang/gui_hr-HR.qm</file>
+ <file>lang/gui_hu-HU.qm</file>
+ <file>lang/gui_id.qm</file>
+ <file>lang/gui_it.qm</file>
+ <file>lang/gui_ja-JP.qm</file>
+ <file>lang/gui_ko.qm</file>
+ <file>lang/gui_lt.qm</file>
+ <file>lang/gui_lv.qm</file>
+ <file>lang/gui_nl-NL.qm</file>
+ <file>lang/gui_no.qm</file>
+ <file>lang/gui_pes-IR.qm</file>
+ <file>lang/gui_pl-PL.qm</file>
+ <file>lang/gui_pt-BR.qm</file>
+ <file>lang/gui_pt-PT.qm</file>
+ <file>lang/gui_ro.qm</file>
+ <file>lang/gui_ru.qm</file>
+ <file>lang/gui_si.qm</file>
+ <file>lang/gui_sk-SK.qm</file>
+ <file>lang/gui_sl-SI.qm</file>
+ <file>lang/gui_sq-AL.qm</file>
+ <file>lang/gui_sr.qm</file>
+ <file>lang/gui_sv.qm</file>
+ <file>lang/gui_th-TH.qm</file>
+ <file>lang/gui_tr-TR.qm</file>
+ <file>lang/gui_uk.qm</file>
+ <file>lang/gui_ur.qm</file>
+ <file>lang/gui_mr.qm</file>
+ <file>lang/gui_vi.qm</file>
+ <file>lang/gui_zh-CN.qm</file>
+ <file>lang/gui_zh-TW.qm</file>
+ <file>lang/Languages.xml</file>
+ <file>icons/16x16/money.png</file>
+ <file>image/spinning-wheel.gif</file>
+ <file>icons/16x16/padlock.png</file>
+ <file>icons/16x16/barrier-transfering.png</file>
+ </qresource>
+</RCC>
diff --git a/src/gui/res/icons/16x16/barrier-connected.png b/src/gui/res/icons/16x16/barrier-connected.png
new file mode 100644
index 0000000..33ea4b4
--- /dev/null
+++ b/src/gui/res/icons/16x16/barrier-connected.png
Binary files differ
diff --git a/src/gui/res/icons/16x16/barrier-connected.xcf b/src/gui/res/icons/16x16/barrier-connected.xcf
new file mode 100644
index 0000000..669663a
--- /dev/null
+++ b/src/gui/res/icons/16x16/barrier-connected.xcf
Binary files differ
diff --git a/src/gui/res/icons/16x16/barrier-disconnected.png b/src/gui/res/icons/16x16/barrier-disconnected.png
new file mode 100644
index 0000000..6ad76e4
--- /dev/null
+++ b/src/gui/res/icons/16x16/barrier-disconnected.png
Binary files differ
diff --git a/src/gui/res/icons/16x16/barrier-disconnected.xcf b/src/gui/res/icons/16x16/barrier-disconnected.xcf
new file mode 100644
index 0000000..9b9c716
--- /dev/null
+++ b/src/gui/res/icons/16x16/barrier-disconnected.xcf
Binary files differ
diff --git a/src/gui/res/icons/16x16/barrier-transfering.png b/src/gui/res/icons/16x16/barrier-transfering.png
new file mode 100644
index 0000000..9ebcdfc
--- /dev/null
+++ b/src/gui/res/icons/16x16/barrier-transfering.png
Binary files differ
diff --git a/src/gui/res/icons/16x16/barrier-transfering.xcf b/src/gui/res/icons/16x16/barrier-transfering.xcf
new file mode 100644
index 0000000..6e5bd2b
--- /dev/null
+++ b/src/gui/res/icons/16x16/barrier-transfering.xcf
Binary files differ
diff --git a/src/gui/res/icons/16x16/money.png b/src/gui/res/icons/16x16/money.png
new file mode 100644
index 0000000..42c52d0
--- /dev/null
+++ b/src/gui/res/icons/16x16/money.png
Binary files differ
diff --git a/src/gui/res/icons/16x16/padlock.png b/src/gui/res/icons/16x16/padlock.png
new file mode 100644
index 0000000..e313e97
--- /dev/null
+++ b/src/gui/res/icons/16x16/padlock.png
Binary files differ
diff --git a/src/gui/res/icons/16x16/warning.png b/src/gui/res/icons/16x16/warning.png
new file mode 100644
index 0000000..193e377
--- /dev/null
+++ b/src/gui/res/icons/16x16/warning.png
Binary files differ
diff --git a/src/gui/res/icons/256x256/barrier.ico b/src/gui/res/icons/256x256/barrier.ico
new file mode 100644
index 0000000..6e90545
--- /dev/null
+++ b/src/gui/res/icons/256x256/barrier.ico
Binary files differ
diff --git a/src/gui/res/icons/64x64/user-trash.png b/src/gui/res/icons/64x64/user-trash.png
new file mode 100644
index 0000000..05eb80e
--- /dev/null
+++ b/src/gui/res/icons/64x64/user-trash.png
Binary files differ
diff --git a/src/gui/res/icons/64x64/video-display.png b/src/gui/res/icons/64x64/video-display.png
new file mode 100644
index 0000000..8a47d90
--- /dev/null
+++ b/src/gui/res/icons/64x64/video-display.png
Binary files differ
diff --git a/src/gui/res/image/about.png b/src/gui/res/image/about.png
new file mode 100644
index 0000000..54d5f07
--- /dev/null
+++ b/src/gui/res/image/about.png
Binary files differ
diff --git a/src/gui/res/image/spinning-wheel.gif b/src/gui/res/image/spinning-wheel.gif
new file mode 100644
index 0000000..220f020
--- /dev/null
+++ b/src/gui/res/image/spinning-wheel.gif
Binary files differ
diff --git a/src/gui/res/lang/Languages.xml b/src/gui/res/lang/Languages.xml
new file mode 100644
index 0000000..5948f9c
--- /dev/null
+++ b/src/gui/res/lang/Languages.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<languages>
+ <language ietfCode="en" name="English" />
+ <language ietfCode="ca-AD" name="Català (Andorra)" />
+ <language ietfCode="cs-CZ" name="Čeština" />
+ <language ietfCode="cy" name="Cymraeg" />
+ <language ietfCode="da" name="Dansk" />
+ <language ietfCode="de" name="Deutsch" />
+ <language ietfCode="es" name="Español" />
+ <language ietfCode="fr" name="Français" />
+ <language ietfCode="hr-HR" name="Hrvatski" />
+ <language ietfCode="id" name="Indonesia" />
+ <language ietfCode="it" name="Italiano" />
+ <language ietfCode="lv" name="Latvijas" />
+ <language ietfCode="lt" name="Lietuvos" />
+ <language ietfCode="hu-HU" name="Magyar" />
+ <language ietfCode="nl-NL" name="Nederlands" />
+ <language ietfCode="no" name="Norsk" />
+ <language ietfCode="pl-PL" name="Polski" />
+ <language ietfCode="pt-PT" name="Português" />
+ <language ietfCode="pt-BR" name="Português (Brasil)" />
+ <language ietfCode="ro" name="Română" />
+ <language ietfCode="sq-AL" name="Shqiptar" />
+ <language ietfCode="sl-SI" name="SlovenÅ¡Äina" />
+ <language ietfCode="sk-SK" name="SlovenÄina" />
+ <language ietfCode="fi" name="Suomi" />
+ <language ietfCode="sv" name="Svenska" />
+ <language ietfCode="vi" name="Tiếng Việt" />
+ <language ietfCode="tr-TR" name="Türkçe" />
+ <language ietfCode="bg-BG" name="българÑки" />
+ <language ietfCode="ru" name="РуÑÑкий" />
+ <language ietfCode="sr" name="ÑрпÑки" />
+ <language ietfCode="uk" name="УкраїнÑький" />
+ <language ietfCode="grk" name="Ελληνικά" />
+ <language ietfCode="he" name="עברית" />
+ <language ietfCode="ar" name="العربية" />
+ <language ietfCode="pes-IR" name="Ùارسی" />
+ <language ietfCode="ur" name="اردو" />
+ <language ietfCode="mr" name="मराठी" />
+ <language ietfCode="si" name="Sඉන්හල" />
+ <language ietfCode="th-TH" name="ภาษาไทย" />
+ <language ietfCode="zh-CN" name="中文 (简体)" />
+ <language ietfCode="zh-TW" name="中文 (ç¹é«”)" />
+ <language ietfCode="ja-JP" name="日本語" />
+ <language ietfCode="ko" name="한국어" />
+</languages>
diff --git a/src/gui/res/lang/gui_af-ZA.qm b/src/gui/res/lang/gui_af-ZA.qm
new file mode 100644
index 0000000..be651ee
--- /dev/null
+++ b/src/gui/res/lang/gui_af-ZA.qm
@@ -0,0 +1 @@
+<¸dÊÍ!¿`¡½Ý \ No newline at end of file
diff --git a/src/gui/res/lang/gui_af-ZA.ts b/src/gui/res/lang/gui_af-ZA.ts
new file mode 100644
index 0000000..446c9a4
--- /dev/null
+++ b/src/gui/res/lang/gui_af-ZA.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="af-ZA" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_ar.qm b/src/gui/res/lang/gui_ar.qm
new file mode 100644
index 0000000..dfc4afa
--- /dev/null
+++ b/src/gui/res/lang/gui_ar.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_ar.ts b/src/gui/res/lang/gui_ar.ts
new file mode 100644
index 0000000..c934dba
--- /dev/null
+++ b/src/gui/res/lang/gui_ar.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="ar" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">عن سينيرجي</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">غير معروÙ</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">الإصدار</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">حسنًا</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">تكوين العمل</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">اختار الامر للعمل</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">اضغط على Ù…Ùتاح التشغيل السريع</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">الاÙراج عنه</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">اضغط واÙرج عن Ù…Ùتاح التشغيل السريع</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">Ùقط على هذه الشاشات</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">تغيير الشاشة</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">تغيير الاتجاه</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">شمال</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">يمين</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">اعلى</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">اسÙÙ„</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">غلق المؤشر لشاشة</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">تبديل</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">تشغيل</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">تعطيل</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">هذا الامر تم عمله عندما</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">Ù…Ùتاح التشغيل السريع ضÙغط</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">Ù…Ùتاح التشغيل السريع اÙرج عنه</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Ù…Ùتاح التشغيل السريع</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">ادخل صÙات Ù…Ùتاح التشغيل السريع:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">ملÙ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">تعديل</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">شباك</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">مساعدة</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">البرناج لا يستطيع ان يبدأ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">عامل التآزر لم يتم العثور عليه</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">لا وجود للتنÙيذ لان عامل التآزر غير موجود.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">المضي٠Ùارغ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished"> الرجاء ملء اسم المضي٠لعاملالتآزر للاتصال.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">لا يمكن كتابة مل٠التكوين</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">غير معروÙ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished"> سينيرجي</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished"> سينيرجي</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">اسم الشاشة:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">خدمة ال IP :</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">تصÙØ­</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">تكوين تبادلي</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">جاهز</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">سجل</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">عنوان ال IP</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">خروج</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">خروج</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">تشغلي</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">توقÙ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">إخÙاء</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">اظهر</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">إعدادات</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">تعديل الإعدادات</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">تشغيلي</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">غير مسمى</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">شمال</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">اليمين الاعلى</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">يسار</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">يمين</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">تصليحات</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">تعديل</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">إعدادات</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">تسجيل الدخول</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">خطأ</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">اللغة:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">تحذير</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">ملاحظة</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">معلومات</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">تصحيح</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">تصحيح1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">تصحيح2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">الرجاء اختيار خيار.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">اهلًا وسهلًا</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">شكرا لتنزيل السنغري</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">التآزر يتيح لك بسهولة مشاركة الماوس ولوحة المÙاتيح بين العديد من أجهزة الكمبيوتر ÙÙŠ مكتبك، وانها لالحرة والمÙتوحة المصدر. مجرد تحرك الماوس من على حاÙØ© شاشة جهاز كمبيوتر واحد على آخر. يمكنك حتى أن حصة كل من الألواح الكتابة الخاصة بك. كل ما تحتاجه هو اتصال بالشبكة. التآزر هو عبر منصة (يعمل على ويندوز، وماكنتوش ولينكس).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">غير معروÙ</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished"> سينيرجي</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_bg-BG.qm b/src/gui/res/lang/gui_bg-BG.qm
new file mode 100644
index 0000000..a93d7af
--- /dev/null
+++ b/src/gui/res/lang/gui_bg-BG.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_bg-BG.ts b/src/gui/res/lang/gui_bg-BG.ts
new file mode 100644
index 0000000..fc6988e
--- /dev/null
+++ b/src/gui/res/lang/gui_bg-BG.ts
@@ -0,0 +1,1410 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="bg-BG" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">За Синерджи</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">ÐеизвеÑтно</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">ВерÑиÑ:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">ОК</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Конфигуриране на дейÑтвие</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Изберете дейÑтвие, което да бъде изпълнено.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">ÐатиÑни горещ клавиш.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">ОтпуÑни горещиÑÑ‚ клавиш.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">ÐатиÑни и пуÑни горещ клавиш</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">Ñамо на тези екрани</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Превключи към екран</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Превключи в поÑока</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">лÑво</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">дÑÑно</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">горе</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">долу</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Заключи курÑора на екрана</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">превключи</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">вкл.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">изкл.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">ДейÑтвието Ñе извършва когато</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">горещиÑÑ‚ клавиш бъде натиÑнат </translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">горещиÑÑ‚ клавиш бъде отпуÑнат.</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Въведи ÑÐ¿ÐµÑ†Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð·Ð° горещ клавиш</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Старт</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">Файл</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">РедакциÑ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">Прозорец</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Помощ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Вашата верÑÐ¸Ñ Ð½Ð° Синерджи не е актуална. ВерÑÐ¸Ñ &lt;b&gt;%1&lt;/b&gt; е доÑтъпна за &lt;a href=&quot;%2&quot;&gt;ÑвалÑне&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Програмата не може да бъде Ñтартирана</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. ÐœÐ¾Ð»Ñ Ð¿Ñ€Ð¾Ð²ÐµÑ€ÐµÑ‚Ðµ дали имате необходимите Ð¿Ñ€Ð°Ð²Ð¾Ð¼Ð¾Ñ‰Ð¸Ñ Ð·Ð° Ñтартиране на тази програма.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Синерджи клиентът не е намерен.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Името на приемника е празно</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">ÐœÐ¾Ð»Ñ Ð¿Ð¾Ð¿ÑŠÐ»Ð½ÐµÑ‚Ðµ име на приемника за да може Ñинерджи клиентът да Ñе Ñвърже.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">КонфигурационниÑÑ‚ файл не може да бъде запиÑан</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">ВременниÑÑ‚ конфигурационен файл, необходим за Ñтартиране на Ñинерджи, не може да бъде запиÑан.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Името на ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð» е невалидно</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Ðе Ñте запиÑали валидно име на ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð» за Ñинерджи Ñървъра. ИÑкате ли да направите това Ñега?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Синерджи Ñървърът не е намерен</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Синерджи беше изключена поради грешка</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Синерджи беше неочаквано изключена Ñ Ð¸Ð·Ñ…Ð¾Ð´ÐµÐ½ код %1.&lt;br&gt;&lt;br&gt; ÐœÐ¾Ð»Ñ Ð·Ð° детайли вижте региÑÑ‚Ñ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð».</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Стоп</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Синерджи Ñе Ñтартира.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Синерджи е активна.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Синерджи не е активна.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">ÐеизвеÑтно</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Синерджи</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Браузвайте за да намерите ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¾Ð½Ð½Ð¸Ñ Ñ„Ð°Ð¹Ð» на програмата</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">ЗапиÑване на конфигурациÑта като...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">ЗапиÑването Ñе провали</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">КонфигурационниÑÑ‚ файл не можа да бъде запиÑан.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Синерджи</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Екранно име:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">IP на Ñървъра:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Старт</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Използвай налична конфигурациÑ:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Конфигурационен файл:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Браузвай...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Интерактивно конфигуриране:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Конфигурирай Ñървър...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Готов</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">РегиÑÑ‚ÑŠÑ€</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Приложи</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP адреÑи:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">ОтноÑно Синерджи</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">Прекрати</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Прекрати</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Стартирай</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Стоп</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Стоп</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Покажи ÑтатуÑ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">Скрий</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Скрий</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">Покажи</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Покажи</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">ЗапиÑване на конфигурациÑта като...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">ЗапиÑване на интерактивно генерираната ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð²ÑŠÐ² файл.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">ÐаÑтройки</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Редактирай наÑтройките</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Стартирай помощник</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Безименен</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">ÐаÑтройка на Синерджи</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Конфигурации на Синерджи (*.sgc);; Ð’Ñички файлове (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Конфигурации на Синерджи (*.conf);; Ð’Ñички файлове (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">СиÑтемниÑÑ‚ контейнер (трей) не е доÑтъпен, затварÑне.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Екранното име липÑва</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Екранното име не може да е празно. ÐœÐ¾Ð»Ñ Ð²ÑŠÐ²ÐµÐ´ÐµÑ‚Ðµ име или откажете диалога.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Екранното име Ñе припокрива Ñ Ð¿Ñевдонима</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Екранното име не може да е Ñъщото като пÑевдонима. ÐœÐ¾Ð»Ñ Ð¿Ñ€ÐµÐ¼Ð°Ñ…Ð½ÐµÑ‚Ðµ пÑевдонима или Ñменете екранното име.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">ÐаÑтройки на екрана</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Екранно име:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">ПÑевдоним</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Добави</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Премахни</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">РегиÑÑ‚ÑŠÑ€:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift (РегиÑÑ‚ÑŠÑ€)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Супер</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">ÐÑма</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Meta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Мъртъв ъгъл</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Горе-лÑво</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Горе-дÑÑно</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Долу-дÑÑно</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Долу-дÑÑно</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Размер на ъглите:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Fixes</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Коригирай клавиш CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Коригирай клавиш NUM LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Коригирай клавиш SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Коригирай XTest за Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Екран &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;За Ñ€ÐµÐ´Ð°ÐºÑ†Ð¸Ñ Ð½Ð° наÑтройките шракнете двукратно&lt;br&gt;За да премахнете екран го привлачете към кошчето</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð½Ð° Ñървъра</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Екрани и препратки</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">За да премахнете екран, го изтеглете към кошчето.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Привлечете към мрежата този бутон, за да добавите нов екран.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Привлачете нови екрани към мрежата или размеÑтете ÑъщеÑтвуващите.
+За да изтриете екран, го привлачете към кошчето.
+Щракнете двукратно върху екрана, за да редактирате наÑтройките му.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Горещ клавиш</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Горещ клавиш</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">Ðово</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">РедакциÑ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Премахни</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">ДейÑтвиÑ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Ðово</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">РедакциÑ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Премахване</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Разширени наÑтройки на Ñървъра</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Превключи</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Превключи Ñлед изчакване</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Превключи при двойно щракане вътре</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Опции</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">ПроверÑвай клиентите на вÑеки</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Използвай отноÑителни Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° мишката</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Синхронизирай ÑкрийнÑейвърите</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Мъртъв ъгъл</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Горе лÑво</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Горе дÑÑно</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Долу лÑво</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Долу дÑÑно</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Размер на ъгъла:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">ЗапиÑване на региÑтъра във файл...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Вдигане на предимÑтвото на Синерджи</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Сигурни ли Ñте че иÑкате да повишите предимÑтвото на Синерджи? Това ще позволи на Синерджи да общува Ñ Ð¿Ñ€Ð¾Ñ†ÐµÑи Ñ Ð¿Ð¾-голÑмо предимÑтво и UAC диалоговите прозорци, но може да предизвика проблеми Ñ Ð¿Ñ€Ð¾Ñ†ÐµÑи Ñ Ð¿Ð¾-ниÑък проритет. Вдигнете предимÑтвото Ñамо ако е наиÑтина необходимо.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">ÐаÑтройки</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Екранно име:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Порт:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">ИнтерфейÑ:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">РегиÑтриране</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Ðиво на региÑтриране:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">РегиÑÑ‚ÑŠÑ€ към файл:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Преглеждане...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Грешка</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Език:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">Други</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Предупреждение</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Бележка</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">ИнформациÑ</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Дебъгване</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Дебъгване1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">ÐаÑтройка на Синерджи</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">ÐœÐ¾Ð»Ñ Ð¸Ð·Ð±ÐµÑ€ÐµÑ‚Ðµ опциÑ.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">ÐœÐ¾Ð»Ñ Ð²ÑŠÐ²ÐµÐ´ÐµÑ‚Ðµ Ð²Ð°ÑˆÐ¸Ñ Ð¸Ð¼ÐµÐ¹Ð» Ð°Ð´Ñ€ÐµÑ Ð¸ парола.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">ÐаÑтройка на Синерджи</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Добре дошли</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Благодарим, че инÑталирахте Синерджи!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Синерджи ти помага леÑно да Ñподелиш мишката и клавиатурата Ñи Ñ Ð¼Ð½Ð¾Ð¶ÐµÑтво компютри на работното ти мÑÑто и е безплатен и Ñ Ð¾Ñ‚Ð²Ð¾Ñ€ÐµÐ½ код. ПроÑто меÑтиш мишка от ръба на екрана на един от компютрите на друг. Можеш да ползваш дори клипборд функциÑта на вÑички компютри Ñвързани чрез Синерджи. Ð’Ñичко, от което имаш нужда е мрежова връзка между компютрите, които ползваш. Синерджи е мултиплатформена (работи на Windows, Mac OS X и Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Сървър или клиент?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">ÐеизвеÑтно</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">ВпиÑването е неуÑпешно, невалидно име или парола.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">ВпиÑването е неуÑпешно, възникна грешка.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">ВпиÑването е неуÑпешно, възникна грешка.
+Сървърът отговарÑ:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Синерджи</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_ca-AD.qm b/src/gui/res/lang/gui_ca-AD.qm
new file mode 100644
index 0000000..45e8f7f
--- /dev/null
+++ b/src/gui/res/lang/gui_ca-AD.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_ca-AD.ts b/src/gui/res/lang/gui_ca-AD.ts
new file mode 100644
index 0000000..3ccc7eb
--- /dev/null
+++ b/src/gui/res/lang/gui_ca-AD.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="ca-AD" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">En quant a Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconegut </translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versió:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Configurar Acció</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Tria l'acció a realitzar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Prem una tecla d'accés ràpid</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Allibera una tecla d'accés ràpid</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Prem i allibera una tecla d'accés ràpid</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">només en aquestes pantalles</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Canviar de pantalla</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Canviar de direcció</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">esquerra</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">dreta</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">amunt</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">avall</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Bloquejar el cursor a la pantalla</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">intercanviar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">on</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">off</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Aquesta acció es realitza quan</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">s'ha premut la tecla d'accés ràpid</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">s'ha allibrerat la tecla d'accés ràpid</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Accés Ràpid</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Entrar l'especificació per la tecla d'accés ràpid</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Engegar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Arxiu</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Editar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Finestra</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Ajuda</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;La teva versió de Barrier està desactualitzada. La nova versió &lt;b&gt;%1&lt;/b&gt; està ara disponible per &lt;a href=&quot;%2&quot;&gt;descarregar&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">El programa no ha pogut iniciar-se</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">L'executable &lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;no ha pogut ser iniciat correctament, tot i que aquest existeix. Si us plau revisa si tens permisos suficients per executar aquest programa.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">No s'ha trobat cap client de Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">L'executable del client de Barrier no existeix.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">El nom de Host està buit</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Si us plau completa el camp nom de host per al client de Barrier per connectar-t'hi.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">No es pot escriure el fitxer de configuració</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">No s'ha pogut escriure al fitxer de configuració temporal requerit per iniciar Barrier </translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Nom del fitxer de configuració invàlid</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">No has omplert un arxiu de configuració vàlid per al servidor de Barrier. Vols veure ara el fitxer de configuració?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">No s'ha trobat el Servidor de Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">L'executable del servidor de Barrier no existeix.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier ha finalitzat degut a un error</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier ha finalitzat inesperadament amb el codi d'error %1.&lt;br&gt;&lt;br&gt;Si us plau mira el log de sortida per més detalls.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">A&amp;turar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier està engegant-se.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier està executant-se.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier no s'està executant.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconegut </translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Mostrar el fitxer de configuració de Sygergy</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Desar configuració com...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Error al guardar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">No es pot guardar la configuració a l'arxiu.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Nom pantalla:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">IP del Servidor:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Engegar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Utilitzar la configuració actual:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Fitxer &amp;configuració:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">&amp;Mostrar...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Configurar interactivament:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">&amp;Configurar Servidor...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Llest</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Log</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Aplicar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">Adreces IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">En &amp;quant a Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Sortir</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Sortir</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Executa</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">A&amp;turar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Aturar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">&amp;Mostrar Estat</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">A&amp;maga</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Amaga</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">Mo&amp;stra</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Mostra</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">&amp;Guardar configuració com...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Guardar la configuració del servidor generada interactivament a un fitxer.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Configuració</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Editar configuració</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Executar Assistent</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Sense nom</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Configuracions Barrier (*.sgc);;Tots els arxius (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Configuracions Barrier (*.conf);;Tots els arxius (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">La safata del sistema no està disponible, sortint.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">El nom de pantalla és buit</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">El nom de pantalla no pot estar buit. Si us plau introdueix un nom o cancel·la el diàleg.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Nom de pantalla coincideix amb l'àlies</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">El nom de pantalla no pot ser el mateix que un àlies. Si us plau treu l'àlies o bé canvia el nom de la pantalla.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Configuració Pantalla</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">&amp;Nom pantalla:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">À&amp;lies</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">A&amp;fegir</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Elimina&amp;r</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">&amp;Modificar claus</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Cap</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Cantonades mortes</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Amunt-esquerra</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Amunt-dreta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Avall-esquerra</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Avall-dreta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Mida cantonada:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">&amp;Fixes</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Arreglar tecla CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Arreglar tecla NUM LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Arreglar tecla SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Arreglar XTest per Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Pantalla: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Doble clic per editar configuració &lt;br&gt;Arrossega la pantalla a la paperera per eliminar-la</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Configuració Servidor</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Pantalles i enllaços</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Arrossega una pantalla de la graella a la paperera per eliminar-la.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Configura la disposició de configuració del teu servidor barrier.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Arrossega aquest botó a la graella per afegir una nova pantalla.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Arrossega noves pantalles a la graella o mou les actuals al voltant.
+Arrossega una pantalla a la paperera per eliminar-la.
+Fes doble clic a una pantalla per editar la seva configuració.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Accés ràpid</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Accés ràpid</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Nou</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Editar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Elimina&amp;r</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">A&amp;ccions</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">No&amp;u</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">E&amp;ditar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Eli&amp;minar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Configuracions avançades del servidor</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Can&amp;viar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Canvia després d'esperar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Canvia a doble &amp;toc a</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Opcions</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Comprova clients cada</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Utilitza moviments relatius ratolí</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Sincronitza protectors de pantalla</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">No prenguis la finestra en primer pla en Windows servers</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Cantonades mortes</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Amunt-&amp;esquerra</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Amunt-&amp;dreta</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Avall-&amp;esquerra</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Avall-&amp;dreta</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Mida ca&amp;ntonada:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Guardar fitxer log a...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Elevar Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Estas segur que vols elevar Barrier?
+Això permet a Barrier interactuar amb processos elevats i el diàleg del UAC, però pot causar problemes amb processos no elevats. Eleva Barrier només si realment ho necessites.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Configuració</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Nom pantalla:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Port:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">&amp;Interfície:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Logging</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Nivell &amp;logging:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Log a fitxer:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Mostrar...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Error</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Idioma:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">Miscel·lània</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Advertència</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Nota</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Info</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Debug</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Debug1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Siusplau tria una opció.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Si us plau introdueix la teva adreça d'email i contrasenya.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Benvingut</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Gràcies per instal·lar Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier et permet compartir fàcilment el ratolí i teclat entre diversos ordinadors al teu escriptori, i és lliure i de codi obert. Només cal moure el punter del ratolí per la vora de la pantalla d'un ordinador a un altre. Fins i tot pots compartir el portapapers. Únicament el que necessites és una connexió de xarxa. Barrier és multiplataforma (funciona en Windows, Mac OS X i Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Servidor o Client?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconegut </translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Ha fallat l'inici de sessió, email o contrasenya incorrectes.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Error inici de sessió, hi ha hagut un error.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Error inici de sessió, hi ha hagut un error.
+Resposta del servidor:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_cs-CZ.qm b/src/gui/res/lang/gui_cs-CZ.qm
new file mode 100644
index 0000000..01a1b70
--- /dev/null
+++ b/src/gui/res/lang/gui_cs-CZ.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_cs-CZ.ts b/src/gui/res/lang/gui_cs-CZ.ts
new file mode 100644
index 0000000..fd639fa
--- /dev/null
+++ b/src/gui/res/lang/gui_cs-CZ.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="cs-CZ" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">O Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Neznámá</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Verze:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Nastavit akci</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Vyberte akci k provedení</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Stisknout klávesovou zkratku</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Pustit (uvolnit) klávesy zkratky</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Stisknout a pustit (uvolnit) klávesy zkratky</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">pouze na těchto obrazovkách</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Přepnout na obrazovku</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Přepnout směrem</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">vlevo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">vpravo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">nahoru</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">dolů</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Uzamknout kurzor k obrazovce</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">přepnout</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">zap</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">vyp</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Tato akce se spustí když</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">je stisknuta klávesová zkratka</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">jsou puštěny (uvolněny) klávesy zkratky</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Klávesová zkratka</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Vytvořte klávesovou zkratku:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Spustit</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">Soubor</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Upra&amp;vit</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">O&amp;kno</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Nápověda</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Používáte starou verzi aplikace Barrier. Ke &lt;a href=&quot;%2&quot;&gt;stažení&lt;/a&gt; je nyní již verze &lt;b&gt;%1&lt;/b&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Program nelze spustit</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Program &lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt; existuje, ale nelze jej spustit. Ověřte, zda máte potřebná (souborová) oprávnění ke spuštění tohoto programu.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Barrier klient nebyl nalezen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Spustitelný soubor s programem Barrier klienta neexistuje.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">NevyplnÄ›n cílový poÄítaÄ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Vyplňte adresu cílového poÄítaÄe ke kterému se Barrier klient má pÅ™ipojit.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Do souboru s nastaveními nelze zapisovat</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Do doÄasného souboru s nastaveními, potÅ™ebného pro spuÅ¡tÄ›ní Barrier, nelze zapisovat.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Neplatný název souboru s nastaveními.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Nevyplnili jste platný soubor s nastaveními pro barrier server. Chcete nyní urÄit umístÄ›ní souboru s nastaveními?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrier server nebyl nalezen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Spustitelný soubor s programem barrier serveru neexistuje.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier bylo ukonÄeno s chybou</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">NeoÄekávané ukonÄení Barrier. BÄ›h skonÄil s kódem %1. &lt;br&gt;&lt;br&gt;Podrobnosti viz protokol.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Za&amp;stavit</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier se spouští.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier je spuštěné.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier není spuštěné.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Neznámá</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Otevřít existující soubor s nastaveními pro barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Uložit nastavení jako…</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Uložení se nezdařilo</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Nelze uložit nastavení do souboru.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Název obrazovky:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">IP &amp;adresa &amp;serveru:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Spustit</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Použít existující nastavení:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Sou&amp;bor s nastaveními:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Pr&amp;ocházet…</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Interaktivní nastavení:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">N&amp;astavit Server…</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Připraven</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Protokol</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">&amp;Použít</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP adresy:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">&amp;O Barrier…</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;UkonÄit</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">UkonÄit</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Spustit</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Zas&amp;tavit</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Zastavit</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Zobrazit stav</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">Skrýt</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Skrýt</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Zobrazit</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Zobrazit</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Uložit nastavení j&amp;ako…</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Uložit interaktivně vytvořené nastavení serveru do souboru.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Nastavení</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Upravit nastavení</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Spustit průvodce</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Nepojmenováno</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Instalovat Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Nastavení Barrier (*.sgc);;Všechny soubory (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Nastavení Barrier (*.conf);;Všechny soubory (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Oznamovací oblast systémové liÅ¡ty je nedostupná, aplikace proto bude ukonÄena.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Název obrazovky nebyl vyplněn</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Název obrazovky nemůže zůstat prázdný. BuÄ jej vyplňte, nebo dialog zruÅ¡te.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Název obrazovky koliduje s jejím alternativním názvem</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Název obrazovky a zástupný název být stejné. BuÄ zástupný název odeberte nebo změňte název obrazovky.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Natavení obrazovky</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Název &amp;plochy</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">A&amp;lternativní názvy</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Přid&amp;at</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Odst&amp;ranit</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">&amp;Modifikátory (klávesy)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Žádné</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Meta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Nevyužité rohy</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Vlevo nahoře</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Vpravo nahoře</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Vlevo dole</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Vpravo dole</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Velikost ro&amp;hu:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">&amp;Opravy</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Oprava chování klávesy Caps Lock</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Oprava chování klávesy Num Lock</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Oprava chování klávesy Scroll Lock</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Oprava chování rozšíření grafického serveru X – XTest a Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Obrazovka: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Dvojklikem upravíte nastavení&lt;br&gt;Přetažením do koše obrazovku smažete</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Nastavení serveru</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Obrazovky a propojení</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Obrazovku odstraníte jejím přetažením z mřížky do koše.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Nastavte rozložení svého nastavení serveru barrier.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Novou obrazovku vytvoříte pÅ™etažením tohoto tlaÄítka do mřížky.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Přetáhněte nové obrazovky do mřížky nebo přesuňte stávající.
+Přetáhnutím obrazovky do koše ji smažete.
+Nastavení obrazovky upravíte dvojklikem na ni.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Klávesové zkratky</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Klávesové zkratky</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Nová</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Upra&amp;vit</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Odst&amp;ranit</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Ak&amp;ce</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">No&amp;vá</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Upravi&amp;t</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Ode&amp;brat</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">PokroÄilé nastavení serveru</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Přepnout</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Přepnout po prodlevě</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Přepnout po dvojitém doteku během</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">M&amp;ožnosti</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Kontrolovat klienty každých</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Používat relativní pohyby myši</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">S&amp;ynchronizovat Å¡etÅ™iÄe obrazovky</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Na serverech s Windows se nepřepínat do popředí</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Nevyužité rohy</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Levý horní</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Pravý &amp;horní</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Spodní levý</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Spodní pravý</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Velikost rohu:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Uložit soubor s protokolem do…</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Zvýšit stupeň oprávnění pro Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Opravdu chcete zvýšit stupeň oprávnění pro Barrier?
+Sice to Barrier umožní pracovat s procesy, které mají také takový stupeň a s dialogem řízení uživatelských úÄtů (UAC), může ale působit problémy aplikacím s běžnými oprávnÄ›ními. Tuto možnost byste tedy mÄ›li využít pouze pokud ji opravdu potÅ™ebujete.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Nastavení</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Název ob&amp;razovky:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">P&amp;ort:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Rozhraní:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Protokolování</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Stupeň podrobnosti protoko&amp;lování:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Protokolovat do souboru:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Procházet…</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Chyba</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">&amp;Jazyk:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">Různé</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Varování</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Poznámka</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Informace</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Ladění</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Ladění1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Ladění2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Instalovat Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Zvolte možnost.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Zadejte svou e-mailovou adresu a heslo.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Instalovat Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Vítejte</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Těší nás, že jste si nainstalovali Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier vám umožní snadno sdílet myÅ¡ a klávesnici napÅ™Ã­Ä vícero poÄítaÄi na vaÅ¡em stole. Aplikace je svobodná a s otevÅ™eným zdrojovým kódem. StaÄí jen pÅ™esunout kurzor myÅ¡i mimo okraj obrazovky jednoho poÄítaÄe na obrazovku dalšího. Mezi poÄítaÄi můžete dokonce sdílet obsahy jejich schránek. Jediné, co je potÅ™eba, je propojení sítí. Barrier je multiplatformní (funguje na Windows, Mac OS X a GNU/Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Server nebo klient?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Neznámá</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">PÅ™ihlášení se nezdaÅ™ilo, e-mailová adresa Äi heslo nebylo zadáno správnÄ›.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Přihlášení se nezdařilo, došlo k chybě.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Přihlášení se nezdařilo, došlo k chybě.
+OdpovÄ›Ä ze serveru:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_cy.qm b/src/gui/res/lang/gui_cy.qm
new file mode 100644
index 0000000..a62d578
--- /dev/null
+++ b/src/gui/res/lang/gui_cy.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_cy.ts b/src/gui/res/lang/gui_cy.ts
new file mode 100644
index 0000000..15b59ea
--- /dev/null
+++ b/src/gui/res/lang/gui_cy.ts
@@ -0,0 +1,1407 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="cy" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Ynghylch Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Fersiwn:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Iawn</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Cyflunio Gweithred</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Dewiswch y weithred i wneud</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Gwasgwch fysell brys</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Ryddhewch y bysell frys</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Gwasgwch a ryddhewch y bysell frys</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">ar y sgriniau yma yn unig</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Newid i sgrîn</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Newid cyfeiriad</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">chwith</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">dde</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">fyny</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">lawr</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Cloi cyrchydd i'r sgrîn</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">toglo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">ymlaen</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">i ffwrdd</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Mae'r weithred hwn yn cael ei wneud gan</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">pan wasgir y bysell frys</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">pan ryddheir y bysell frys</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Bysell frys</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Rhowch y manyleb ar gyfer y bysell frws hwn:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Dechrau</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">Ffeil</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Golygu</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">Ffenest</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Cymorth</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Methwyd cychwyn y rhaglen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Nid oedd yn bosib cychwyn y rhaglen &lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;yn llwyddiannus er ei fod yn bodoli. Gwnewch yn siwr fod ganddoch yr hawliau cywir i redeg y rhaglen yma.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Methwyd dod o hyd i gleient Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Nid yw'r ffeil weithredadwy ar gyfer y cleient barrier yn bodoli.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Mae'r enw gwesteiwr yn bodoli</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Rhowch enw gwesteiwr i'r cleient barrier i gysylltu iddo.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Methu sgrifennu i'r ffeil gyfluniad</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Nid yw'n bosib sgrifennu i'r ffeil gyfluniad dros dro sydd angen i ddechrau barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Mae enw'r ffeil gyfluniad yn annilys</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Nid ydych wedi llenwi mewn ffeil gyfluniad dilys ar gyfer y gweinydd barrier. Hoffech chi bori am ffeil gyfluniad nawr?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Methwyd dod o hyd i weinydd Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Nid yw'r ffeil weithredadwy ar gyfer y gweinydd barrier yn bodoli.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Fe wnaeth Barrier derfynnu gyda gwall</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Fe wnaeth Barrier derfynnu yn annisgwyl gyda cod gorffen %1.&lt;br&gt;&lt;br&gt;Gwelwch allbwn log am fanylion.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Stopio</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Mae Barrier yn dechrau.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Mae Barrier yn rhedeg.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Nid yw Barrier yn rhedeg.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Profi am ffeil gyfluniad barriers</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Cadw'r cyfluniad fel...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Methwyd cadw</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Methwyd cadw'r ffeil gyfluniad</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Dechrau</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Defnyddio'r cyfluniad presennol:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Ffeil Cyfluniad:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Pori...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Cyflunio'n rhyngweithiol:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Cyflunio Gweinydd...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Yn barod</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Log</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Ynghylch Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">Gadael</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Gadael</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Rhedeg</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Stopio</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Stopop</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Dangos Statws</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Cadw'r cyfluniad fel...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Cadw'r cyfluniad gweinydd a gynhyrchwyd i ffeil:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Gosodiadau</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Golygu gosodiadau</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Rhedeg Dewin</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Heb ei enwi</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Gosod fyny Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Cyfluniadau Barrier (*.sgc);;Pob ffeil (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Cyfluniadau Barrier (*.conf);;Pob ffeil (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Nid yw'r gilfach system ar gael, yn gadael.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Mae enw'r sgrîn yn wag</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Gosodiadau Sgrîn</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Enw sgrîn</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Ffugenwau</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Ychwanegu</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Dileu</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Bysellau newid</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Dim</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Top-chwith</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Top-dde</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Gwaelod-chwith</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Gwaelod-dde</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Maint Cornel:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Cyfluniad Gweinydd</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Sgriniau a dolenni</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Llusgwch sgrîn o'r grid neu i'r bin sbwriel i'w ddileu.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Cyfluniwch gynllun eich cyfluniad gweinydd barrier</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Llusgwch y botwm i'r grid i ychwanegu sgrîn newydd.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Llusgwch sgrîn newydd i'r grid neu symudwch y rhai presennol o gwmpas.
+Llusgwch sgrîn i'r bin sbwriel i'w ddileu.
+Rhowch glic dwbl ar sgrîn i newid ei osodiadau.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Bysellau brys</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Bysellau brys</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">Newydd</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Golygu</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Dileu</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Gweithredoedd</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Newydd</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Golygu</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Dileu</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Gosodiadau uwch y gweinydd</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Newid</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Newid ar ôl aros</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Newid ar ôl tap dwl yn</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Dewisiadau</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Gwirio cleientiaid bob</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Defnyddio symudiadau llygoden perthynol</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Gosodiadau</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Pori...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Gwall</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Rhybudd</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Nodyn</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Gwybodaeth</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Dadfygio</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Gosod fyny Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Dewiswch.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Gosod fyny Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Mae Barrier yn gadael i chi rhannu eich llygoden a'r bysellfwrdd yn hawdd rhwng nifer o gyfrifiaduron ar eich desg, ac mae'n rhad ac am ddim a Ffynhonnell Agored. Dyna gyd sydd angen yw symud eich llygoden dros ymyl sgrîn un cyfrifiadur i un arall. Gallwch hyd yn oed rannu eich holl clipfyrddau. Y cyfan sydd ei angen yw cysylltiad rhwydwaith. Mae Barrier yn draws-lwyfan (mae'n gweithio ar Windows, Mac OS X a Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Gweinydd neu Gleient?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_da.qm b/src/gui/res/lang/gui_da.qm
new file mode 100644
index 0000000..280988f
--- /dev/null
+++ b/src/gui/res/lang/gui_da.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_da.ts b/src/gui/res/lang/gui_da.ts
new file mode 100644
index 0000000..d910ccb
--- /dev/null
+++ b/src/gui/res/lang/gui_da.ts
@@ -0,0 +1,1410 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="da" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Om Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Ukendt</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Version:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Konfigurer handling</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Vælg handling til udførelse</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Tast en hotkey</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Slip en hotkey</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Tryk og slip en hotkey</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">kun på disse skærmbilleder</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Skift til skærm</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Skift i retning</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">venstre</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">højre</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">op</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">ned</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Lås markør til skærm</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">skift mellem</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">til</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">fra</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Denne handling udføres, når</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">hotkey'en er trykket</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">hotkey'en er sluppet</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Hotkey</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Indtast beskrivelse af hotkey'en:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Fil</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Rediger</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Vindue</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Hjælp</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Din version af Barrier er forældet. Version &lt;b&gt;%1&lt;/b&gt; er nu tilgængelig som &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Programmet kan ikke startes</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Programfilen&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;kunne ikke startes, eller den eksisterer ikke. Kontroller, at du har rettigheder til at køre programmet.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Barrier-klienten blev ikke fundet</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Programfilen til Barrier-klienten eksisterer ikke.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Værtsnavnet er ikke angivet</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Indtast det værtsnavn, som Barrier-klienten skal forbinde sig til.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Kan ikke skrive til konfigurationsfilen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Den midlertidige konfigurationsfil, som skal bruges for at starte Barrier, kan ikke oprettes.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Ugyldigt navn for konfigurationsfilen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Du har ikke oprettet en gyldig konfigurationsfil til Barrier-serveren. Vil du søge efter konfigurationsfilen?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrier-serveren blev ikke fundet</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Programfilen til Barrier-klienten eksisterer ikke.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier afsluttede med en fejl</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier afsluttede uventet med fejlkoden %1.&lt;br&gt;&lt;br&gt;Se venligst logfilen for detaljer.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Stop</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier starter.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier kører allerede.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier er ikke startet</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Ukendt</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Søg efter en Barrier-konfigurationsfil</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Gem konfiguration som...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Gem fejlede</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Kunne ikke gemme konfigurationen i filen.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Skærmnavn:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">&amp;Server-IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Brug eksisterende konfiguration:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">&amp;Konfigurations fil:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">&amp;Gennemse...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Interaktiv konfiguration:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">&amp;Konfigurer Server...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Klar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Log</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">&amp;Anvend</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP-adresser:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">&amp;Om Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Afslut</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Afslut</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Kør</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">S&amp;top</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Stop</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">V&amp;is Status</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Skjul</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Skjul</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Vis</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Vis</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Gem konfiguration &amp;som...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Gem den interaktivt genererede server-konfiguration i en fil.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Indstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Rediger indstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Kør guide</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Unavngivet</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Opsæt Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier-konfigurationer (*.sgc);;Alle filer (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier-konfigurationer (*.conf);;Alle filer (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Systembakken er ikke tilgængelig. Afslutter.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Skærmnavn er ikke angivet</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Skærm navnet kan ikke være tomt. Enten skriv et navn eller annuller denne dialog.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Skærm navnet matcher alias</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Skærm navnet kan ikke være det samme som et alias. Enten fjern alias'et eller skift skærm navnet.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Skærmindstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Skærm&amp;navn:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">A&amp;liaser</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;Tilføj</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Fjern</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">&amp;Ændre-taster</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Skift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Skift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Ingen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Døde hjørner</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Øverst til venstre</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Øverst til højre</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Nederst til venstre</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Nederst til højre</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Hjørnest&amp;ørrelse:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">&amp;Rettelser</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Fix CAPS LOCK-tasten</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Fix NUM LOCK-tasten</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Fix SCROLL LOCK-tasten</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Fix Xtest for Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Skærm: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Dobbeltklik for at redigere indstillinger&lt;br&gt;Træk skærm til Papirkurven for at slette den</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Server-konfiguration</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Skærme og links</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Slet en skærm ved at trække den fra gitteret over i Papirkurven.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Indstil Barrier-serverens konfigurations-layout.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Træk denne knap over på gitteret for at tilføje en ny skærm.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Træk nye skærme over på gitteret eller ryk eksisterende skærme rundt
+slet en skærm ved at trække den over i Papirkurven.
+Dobbeltklik på skærmen for at redigere indstillingerne.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Hotkeys</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Hotkeys</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Ny</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Rediger</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Fjern</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">H&amp;andlinger</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">N&amp;y</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">R&amp;ediger</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">S&amp;let</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Avancerede server-indstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">&amp;Skift</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Skift &amp;efter at have ventet</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Aktivér dobbelt &amp;tap inden for</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Indstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">&amp;Check klienter hver</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Brug &amp;relative museflyt</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">S&amp;ynkroniser screen-savere</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Tag ikke &amp;forgrundsvinduer på Windows-servere</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Døde hjørner</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Øv&amp;erst til venstre</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Øverst til hø&amp;jre</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">&amp;Nederst til venstre</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Nederst til hø&amp;jre</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Hjø&amp;rnestørrelse:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Gem logfil i:</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Ophøj Barrier's rettigheder</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Er du sikker på, at du vil ophøje Barrier's rettigheder? Det giver Barrier rettigheder til at interagere med andre ophøjede processer og UAC-dialogboksen, men det kan give problemer med processer, som ikke har samme rettigheder. Ophøj kun Barrier, hvis du virkelig har brug for det.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Indstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Sk&amp;rmnavn:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">P&amp;ort:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">&amp;Brugerflade</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Logning</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">&amp;Lognings niveau:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Log til fil:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Gennemse...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Fejl</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Sprog:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">Diverse</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Advarsel</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Bemærk</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Information</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Fejlfind</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Fejlfind1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Fejlfind2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Opsæt Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Vælg en mulighed.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Vær sød at skrive din email adresse og adgangskode.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Opsæt Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Velkommen</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Tak fordi du installerede Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier lader dig nemt dele din mus og tastatur mellem flere computere på dit skrivebord, og det er gratis og Open Source. Flyt blot din mus forbi skærmkanten på en computers skærm til en anden. Du kan endda dele din udklipsholder også. Det eneste du behøver er en netværksforbindelse. Desuden fungere Barrier på flere platforme (Virker på Windows, Mac OS X samt Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Server eller Klient?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Ukendt</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Fejl i login, forkert email eller adgangskode.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Fejl i login, en fejl opstod.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Fejl i login, en fejl opstod.
+Server svar:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_de.qm b/src/gui/res/lang/gui_de.qm
new file mode 100644
index 0000000..095a698
--- /dev/null
+++ b/src/gui/res/lang/gui_de.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_de.ts b/src/gui/res/lang/gui_de.ts
new file mode 100644
index 0000000..4b826f5
--- /dev/null
+++ b/src/gui/res/lang/gui_de.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="de" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Ãœber Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Unbekannt</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Version:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Aktion konfigurieren</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Wähle eine Aktion, die ausgeführt werden soll</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Hotkey drücken</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Hotkey loslassen</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Hotkey drücken und loslassen</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">Nur auf diesen Bildschirmen</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Zu Anzeige Wechseln</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">In Richtung wechseln</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">links</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">rechts</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">hoch</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">runter</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Cursor auf Anzeige beschränken</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">umschalten</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">ein</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">aus</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Diese Aktion wird ausgeführt, wenn</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">wenn der Hotkey gedrückt wird</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">wenn der Hotkey losgelassen wird</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Hotkey</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Gib die Definition für den Hotkey ein:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Datei</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Bearbeiten</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Fenster</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Hilfe</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Ihre Barrier Version ist veraltet. Version &lt;b&gt;%1&lt;/b&gt; ist jetzt zum &lt;a href=&quot;%2&quot;&gt;Download&lt;/a&gt; verfügbar.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Das Programm konnte nicht gestartet werden</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Die Anwendung&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt; konnte nicht gestartet werden, obwohl sie vorhanden ist. Bitte überprüfen sie, ob sie die benötigten Berechtigungen zur Ausführung der Anwendung haben,</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Der Barrier Client wurde nicht gefunden</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Die ausführbare Datei für den Barrier Client existiert nicht.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Der Hostname is leer</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Bitte tragen Sie einen Hostnamen ein, zu dem sich der Barrier-Client verbinden soll.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Konfigurationsdatei konnte nicht geschrieben werden</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Die temporäre Konfigurationsdatei konnte nicht geschrieben werden. Sie wird jedoch für den Start von Barrier benötigt.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Der Dateiname der Konfigurationsdatei ist ungültig.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Sie haben keine gültige Konfigurationsdatei angegeben. Wollen sie jetzt nach dieser Datei suchen?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Der Barrier Server wurde nicht gefunden.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Die ausführbare Datei für den Barrier Server existiert nicht.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier wurde mit einem Fehler beendet.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier wurde unerwartet mit Abbruchcode %1 beendet. &lt;br&gt;&lt;br&gt; Weitere Informationen können dem Log entnommen werden.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Stopp</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier wird gestartet.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier läuft.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier wird nicht ausgeführt.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Unbekannt</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Nach einer Konfigurationsdatei für Barrier suchen.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Konfiguration speichern unter ...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Speichern fehlgeschlagen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Konfiguration konnte nicht in Datei gespeichert werden</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Anzeigename:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">Server IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Verwende bestehende Konfiguration:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">&amp;Konfigurationsdatei:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">&amp;Durchsuchen...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Interaktiv konfigurieren:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Server &amp;konfigurieren...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Fertig</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Log</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">&amp;Anwenden</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP Adressen:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">&amp;Ãœber Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Beenden</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Beenden</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Start</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">S&amp;topp</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Stop</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">S&amp;tatus anzeigen</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Verstecken</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Verstecken</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Zeigen</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Anzeigen</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Configuration speichern &amp;unter ...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Speichere die interaktiv erstellte Konfiguration in eine Datei.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Einstellungen</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Einstellungen bearbeiten</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Assistent ausführen</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Unbenannt</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier einrichten</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier Konfigurationen (*.sgc);;Alle Dateien (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier Konfigurationen (*.conf);;Alle Dateien (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Infobereich ist nicht verfügbar. Beende Programm.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Der Anzeigename ist leer</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Der Bildschirmname darf nicht leer sein. Bitte trage einen Namen ein oder schließe das Fenster.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Der Anzeigename passt zum Alias</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Der Anzeigename kann nicht derselbe sein wie der Alias. Bitte entfernen sie den Alias oder ändern sie den Anzeigenamen.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Anzeigeeinstellungen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Anzeige&amp;name:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">A&amp;liase</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;Hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Entfernen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">&amp;Zusatztasten</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Um&amp;schalt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Umschalt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Strg</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Windows</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Keine</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Strg:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&quot;&amp;Tote&quot; Ecken</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Oben-links</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Oben-rechts</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Unten-links</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Unten-rechts</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">&amp;Größe:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">&amp;Korrekturen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Korrektur für Feststelltaste</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Korrektur für Num Lock</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Korrektur für Scroll Lock</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Korrektur für XTest mit Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Anzeige: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Klicken Sie doppelt um die Einstellungen zu ändern&lt;br&gt;Ziehen Sie die Anzeige in den Papierkorb um sie zu entfernen</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Server Konfiguration</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Anzeigen und Verbindungen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Ziehen Sie eine Anzeige vom Raster in den Papierkorb um sie zu entfernen.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Konfigurieren Sie die Anordnung Ihrer Barrier Server Konfiguration.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Ziehen diese Symbol auf das Raster um eine neue Anzeige hinzuzufügen.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Ziehen Sie neue Anzeigen auf das Raster oder verschieben sie existierende.
+Ziehen Sie eine Anzeige in den Papierkorb um sie zu entfernen.
+Klicken sie doppelt auf eine Anzeige um die Einstellungen zu bearbeiten.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Hotkeys</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Hotkeys</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Neu</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Bearbeiten</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Entfernen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">&amp;Befehle</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Ne&amp;u</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Än&amp;dern</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">&amp;Entfernen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Erwiterte Servereinstellungen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Wech&amp;sel</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Wechsel n&amp;ach Wartezeit</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Wechsel nach doppel&amp;ter Randberührung innerhalb von</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Optionen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Prüfe auf Meldungen vom &amp;Client aller</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Ve&amp;rwende relative Mausbewegungen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Bildschirmschoner s&amp;ynchronisieren</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Auf Windows Servern &amp;Fenster im Vordergrund nicht aktivieren</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&quot;&amp;Tote&quot; Ecken</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">O&amp;ben-links</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Oben-rec&amp;hts</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">&amp;Unten-links</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Unten-rec&amp;hts</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">&amp;Größe:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Speicherort des Logfiles</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Barrier Befördern</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Sind Sie sicher das Sie Barrier Erweiterte Benutzerrechte einräumen wollen?
+Das erlaubt Barrier mit Prozessen die höhere Rechte haben und dem UAC-Dialog zu interagieren, kann aber bei normalen Prozessen Probleme verursachen. Erweiterte Rechte an Barrier bitte nur vergeben wenn es unbedingt nötig ist.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Einstellungen</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">&amp;Anzeigename:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">P&amp;ort:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Schn&amp;ittstelle</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Protokollierung</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">&amp;Umfang:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">In Datei:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Durchsuchen...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Fehler</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Sprache:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">&amp;Sonstiges</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Warnung</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Hinweis</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Info</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Debug</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Debug1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier einrichten</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Bitte wählen Sie eine option aus.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Bitte geben Sie Ihre E-Mail Adresse und Ihr Passwort ein.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier einrichten</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Willkommen</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Danke, dass du Barrier installiert hast!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Mit Barrier können Sie einfach Ihre Tastatur und Maus an mehreren Computern auf Ihrem Schreibtisch nutzen, und es ist Frei und Open Source. Bewegen Sie einfach ihre Maus über den Rand des Bildschirms eines Computers auf den Bildschirm eines anderen. Sie können sogar den Inhalt ihrer Zwischenablage an alle Computer verteilen. Alles was Sie brauchen ist ein Netzwerk-Anschluss. Barrier funktioniert auf Windows, Mac OS X und Linux.</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Server oder Client?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Unbekannt</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Login fehlgeschlagen, falsche E-Mail Adresse oder Passwort.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Login fehlgeschlagen, ein Fehler ist aufgetreten.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Login fehlgeschlagen, ein Fehler ist aufgetreten.
+Serverantwort:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_es.qm b/src/gui/res/lang/gui_es.qm
new file mode 100644
index 0000000..f9057ee
--- /dev/null
+++ b/src/gui/res/lang/gui_es.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_es.ts b/src/gui/res/lang/gui_es.ts
new file mode 100644
index 0000000..98ee4f9
--- /dev/null
+++ b/src/gui/res/lang/gui_es.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="es" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Acerca de Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconocido</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versión</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Configurar Acción</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Elige la acción a realizar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Presiona una tecla de acceso directo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Al levantar ó liberar el accedo directo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Al pulsar y liberar el acceso directo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">sólo en estas pantallas</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Cambiar a pantalla</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Cambiar dirección</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">izquierda</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">derecha</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">arriba</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">abajo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Bloquear cursor en pantalla</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">alternar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">activado</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">desactivado</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Esta acción se realiza cuando</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">la tecla de acceso directo es presionada</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">la tecla de acceso directo es soltada</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Tecla de acceso directo</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Ingresa la especificación para la tecla de acceso directo:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Iniciar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Archivo</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Edición</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Ventana</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Ayuda</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Tú versión de Barrier es antigua. La versión &lt;b&gt;%1&lt;/b&gt; esta ahora disponible para &lt;a href=&quot;%2&quot;&gt;descargar&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">El programa no se puede iniciar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">El ejecutable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;no se pudo iniciar exitósamente, aunque sí existe. Por favor, revisa si tienes permisos suficientes para ejecutar este programa.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">No se encontró el cliente Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">El ejecutable para el cliente Barrier no existe.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Nombre de host está vacío</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Por favor, ingresa un nombre de host al que se conectará el cliente Barrier.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">No se puede escribir el archivo de configuración</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">El archivo de configuración temporal necesario para iniciar Barrier no puede ser escrito.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Nombre de archivo de configuración inválido</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">No has ingresado un archivo de configuración válido para el servidor Barrier. Queres buscar el archivo de configuración ahora?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">No se encontró el servidor Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">El ejecutable para el servidor Barrier no existe.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier terminó con un error</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Synergu terminó inesperadamente con el código de salida %1.&lt;br&gt;&lt;br&gt;Por favor, vea el registro de eventos para detalles.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Detener</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier está iniciando.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier se está ejecutando.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier no se está ejecutando.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconocido</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Buscar un archivo de configuración Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Guardar configuración como...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">No se guardó</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">No se pudo guardar la configuración en el archivo.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Nombre en pantalla:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">IP del servidor:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Iniciar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Usar configuración existente:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">&amp;Configuración:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">&amp;Buscar...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Configurar interactivamente:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">&amp;Configurar Servidor...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Listo</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Registro</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Aplicar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">Direcciones IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">&amp;Acerca de Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Salir</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Salir</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Ejecutar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">De&amp;tener</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Detener</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">&amp;Mostrar Estado</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Ocultar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Ocultar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Mostrar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Mostrar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Gu&amp;ardar configuración como...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Guardar la configuración generada interactivamente en un archivo.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Opciones</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Editar Opciones</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Iniciar instalación</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Sin nombre</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Configuraciones Barrier (*.sgc);;Todos los archivos (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Configuraciones Barrier (*.conf);;Todos los archivos (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">El área de notificación no esta disponible, cerrando.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Nombre de pantalla está vacío</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">El nombre de la pantalla no puede estar vació. Por favor introduce un nombre o cancela este dialogo.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">El nombre de la pantalla coincide con el alias</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">El nombre de la pantalla no puede ser el mismo que un alias. Elimina el alias o cambia el nombre de pantalla por favor.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Opciones de Pantalla</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">&amp;Nombre de Pantalla</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">&amp;Alias</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;Agregar</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Elimina&amp;r</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">&amp;Modificar teclas</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Mayúsculas</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Ninguno</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Esquinas &amp;Desactivadas</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Arriba-Izquierda</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Arriba-Derecha</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Abajo-Izquierda</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Abajo-Derecha</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">&amp;Tamaño de Esquina:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">&amp;Reparaciones</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Reparar tecla CAPS LOCK (BLOQ MAYÚS)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Reparar tecla NUM LOCK (BLOQ NUM)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Reparar tecla SCROLL LOCK (BLOQ DESPL)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Reparar XTest para Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Pantalla: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Haz doble clic para editar opciones&lt;br&gt;Arrastra la pantalla a la papelera para borrarla</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Configuración de Servidor</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Pantallas y enlaces</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Mueve una pantalla de la rejilla a la papelera para eliminarla.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Elige el diseño de rejilla para la configuración de tu servidor Barrier.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Mueve este botón a la rejilla para añadir una nueva pantalla.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Arrastra nuevas pantallas hacia la rejilla o mueve las ya existentes.
+Mueve una pantalla a la papelera para eliminarla.
+Haz doble click sobre una pantalla para modificar sus ajustes.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Teclas de acceso directo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Teclas de acceso directo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Nueva</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Edición</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Elimina&amp;r</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">&amp;Acciones</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">&amp;Nueva</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">&amp;Editar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">&amp;Borrar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Opciones Avanzadas de Servidor</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">&amp;Cambiar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Cambiar &amp;al esperar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Cambiar doble toque en</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Opciones</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Revisar &amp;clientes cada</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">&amp;Sar movimientos &amp;relativos del mouse</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">&amp;Sincronizar protectores de pantalla</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">No tomar la ventana de primer plano en servidores Windows</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Esquinas &amp;Desactivadas</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">&amp;Arriba-Izquierda</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">&amp;Arriba-Derec&amp;ha</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">&amp;Bajo-Izquierda</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">&amp;Abajo-Derecha</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">&amp;Tamaño de Esqui&amp;na:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Guardar archivo de registro en...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Elevar a Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">¿Estás seguro que quieres elevar a Barrier?
+Esto permitirá que puedas interactuar con procesos elevados y el cuadro de diálogo de UAC, pero puede causar problemas con procesos no elevados. Eleva a Barrier solo si realmente lo necesitas.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Opciones</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">&amp;Nomb&amp;re de Pantalla:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Puert&amp;o:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">&amp;Interfaz:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Registro</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Nive&amp;l de Registro:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Guardar registro en archivo:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Examinar...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Error</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Idioma:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">Misceláneos
+</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Advertencia</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Nota</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Información</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Depuración</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Depuración1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Depuración2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Elige una opción, por favor.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Introduce tu dirección de correo electrónico y contraseña</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Bienvenido</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">¡Gracias por instalar Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier te permite compartir fácilmente tu mouse y teclado entre múltiples computadores en tu escritorio, es Gratis y es de Código Abierto. Solo mueve tu mouse hacia los bordes de la pantalla de uno de los computadores hacia otro. Puedes incluso compartir tu portapapeles. Todo lo que necesitas es una conexión de red. Barrier es multiplataforma (funciona en Windows, Mac OS X y Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">¿Servidor o Cliente?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconocido</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Inicio de sesión incorrecto, email o contraseña inválidos.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Inicio de sesión fallido, se encontró un error.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Ha fallado el inicio de la sesión, ocurrió un error.
+Respuesta del servidor: %1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_et-EE.qm b/src/gui/res/lang/gui_et-EE.qm
new file mode 100644
index 0000000..2ff9398
--- /dev/null
+++ b/src/gui/res/lang/gui_et-EE.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_et-EE.ts b/src/gui/res/lang/gui_et-EE.ts
new file mode 100644
index 0000000..1e1ea57
--- /dev/null
+++ b/src/gui/res/lang/gui_et-EE.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="et-EE" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Barrier'st</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Tundmatu</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versioon:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Tegevuse seadistamine</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Vali teostatav tegevus</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Vajuta kiirklahv alla</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Vabasta kiirklahv</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Vajuta kiirklahv alla ja vabasta</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">ainult nendel ekraanidel</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Lülitu ekraanile</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Lülitu suunas</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">vasak</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">parem</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">üles</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">alla</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Lukusta kursor ekraanile</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">lülita</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">sisse</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">välja</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">See toiming teostatakse kui</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">Kiirklahv on alla vajutatud</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">kiirklahv on vabastatud</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Kiirklahv</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Sisesta kiirklahvi täpsemad seaded:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Käivita</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">Fail</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Redigeeri</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">Aken</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Abi</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Teie Barrier versioon on aegunud . Uus versioon &lt;b&gt;%1&lt;/b&gt; on saadaval &lt;a href=&quot;%2&quot;&gt;allalaadimiseks&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Rakendus ei suuda käivituda</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Käivitusfaili&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;ei õnnestunud käivitada, või pole seda olemas. Palun kontrollige, kas Teil on piisavalt õiguseid selle toimingu teostamiseks..</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Barrier klienti ei leitud</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Barrier kliendi käivitusfaili ei leitud.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Tugijaama nimi on tühi</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Palun sisestage tugijaama nimi millega barrier klient peab ühenduma.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Ei saa kirjutada seadete faili.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Barrier käivitamiseks vajaliku ajutise seadete faili kirjutamine ebaõnnestus.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Seadete faili nimi on vigane</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Te pole valinud korrektset seadete faili barrier serverile. Kas soovite valida seadete faili nüüd?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrier serverit ei leitud</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Barrier serveri käivitusfaili ei leitud.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier katkestas töö veateatega</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier katkestas ootamatul töö veateatega %1.&lt;br&gt;&lt;br&gt;Täpsema info saamiseks kontrollige väljundlogi.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Peata</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier käivitub.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier töötab</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier ei tööta.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Tundmatu</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Vali barrier seadete fail</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Salvesta seaded kui...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Salvestamine ebaõnnestus</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Seadete salvestamine faili ebaõnnestus.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Ekraani nimi:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">Serveri IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Käivita</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Kasuta olemasolevat seadistust:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Seadete fail:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Vali</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Seadistage interaktiivselt:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Serveri seadistamine...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Valmis</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Logi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Rakenda</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP aadressid:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Barrier'st...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">Välju</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Välju</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Käivita</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Peata</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Peata</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Näita olekut</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">Peida</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Peida</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">Näita</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Näita</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Salvesta seaded kui...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Salvesta interakttivselt loodud serveri seaded faili.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Seaded</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Seadete muutmine</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Käivita nõustaja</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Nimetu</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier seadistamine</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier seaded (*.sgc);;kõik failid (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier seaded (*.conf);;Kõik failid (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Süsteemisalv pole kättesaadav, loobun.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Ekraani nimi on tühi</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Ekraani nimi peab olema määratud. Palun määrake nimi või katkestage dialoog.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Ekraani nimi ühildub aliasega</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Ekraani nimi peab olema erinev aliasest. Palun eemaldage alias või muutke ekraani nime.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Ekraani seaded</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Ekraani nimi:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Aliased</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Lisa</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Eemalda</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Muuteklahvid</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Puudub</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Meta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Surnud nurgad</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Ãœlemine-vasak</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Ãœlemine-parem</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Alumine vasak</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Alumine-parem</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Nurga suurus:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Parandused</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Kinnita CAPS LOCK klahv</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Kinnita NUM LOCK klahv</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Kinnita SCROLL LOCK klahv</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Kinnita XTest Xineramale</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Ekraan: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Topeltklõps seadete muutmiseks&lt;br&gt;Eemaldamiseks lohista ekraan prügikasti</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Serveri seaded</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Ekraanid ja seosed</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Eemaldamiseks lohista ekraan joonestikult prügikasti.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Seadista barrier serveri paigutuse seadeid.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Ekraani lisamiseks lohista see nupp joonestikule.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Lohista uued ekraanid joonestikule või liiguta olemasolevaid.
+Eemaldamiseks lohista ekraan prügikasti.
+Ekraani seadete muutmiseks tee sellel topeltklõps.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Kiirklahvid</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Kiirklahvid</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">Uus</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Redigeeri</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Eemalda</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Toimingud</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Uus</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Muuda</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Eemalda</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Täpsemad serveri seaded</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Lülitu</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Lülitu peale ootamist</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Lülitu peale topeltkoputust ajaga</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Valikud</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Kontrolli klienti peale iga</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Kasuta relatiivseid hiire liikumisi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Sünkrooni ekraanisäästjad</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Ära võta esiplaani akent Windows serverites</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Surnud nurgad</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Ãœlemine vasak</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Ãœlemine-parem</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Alumine-vasak</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Alumine-parem</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Nurga suurus</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Salvesta logifail asukohta...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Barrier ülendamine</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Kas soovite kindlasti Barrier't ülendada?
+See võimaldab Barrier'l toimetada ülendatud protsessidega ja UAC dialoogiga, kuid võib tekitada probleeme ülendamata protsessidega. Ülendage Barrier ainult kindlal vajadusel.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Seaded</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Ekraani nimi:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Port:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Seade:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Logimine</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Logimise tase:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Logi faili:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Vali...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Viga</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Keel:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">Mitmesugust</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Hoiatus</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Märkus</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Info</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Debug</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Debug1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier seadistamine</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Palun valida sobiv funktsioon.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Palun sisestage oma uus salasõna.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier seadistamine</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Tere tulemast</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Täname, et installeerisid Sünergia!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier võimaldab lihtsalt jagada oma klaviatuuri ja hiirt mitme arvuti vahel, see on tasuta ja vabavaraline. Lihtsalt liiguta oma hiir üle ühe arvuti ekraaniserva teise arvuti ekraanile. Võite isegi jagada kõiki oma lõikepuhvreid. Vajad vaid võrgu ühendust. Barrier't saab kasutada erinevates operatsioonisüsteemides (Windows, Mac OS X ja Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Server või klient?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Tundmatu</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Logimine ebaõnnestus, vigane e-posti aadress või salasõna.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Logimine ebaõnnestus, tekkis viga.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Logimine ebaõnnestus, tekkis viga.
+Server vastas:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_fi.qm b/src/gui/res/lang/gui_fi.qm
new file mode 100644
index 0000000..46f69c1
--- /dev/null
+++ b/src/gui/res/lang/gui_fi.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_fi.ts b/src/gui/res/lang/gui_fi.ts
new file mode 100644
index 0000000..9fe0b60
--- /dev/null
+++ b/src/gui/res/lang/gui_fi.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="fi" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Tietoa Barrierstä</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Tuntematon</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versio:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">OK</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Määritä toiminto</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Määritä suoritettava toiminto</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Paina pikanäppäin pohjaan</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Vapauta pikanäppäin</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Paina ja vapauta pikanäppäin</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">vain näillä näytöillä</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Vaihda näyttöön</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Vaihda</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">vasemmalle</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">oikealle</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">ylös</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">alas</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Lukitse kursori näytölle</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">kytke päälle/pois</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">päälle</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">pois päältä</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Suorita toiminto</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">kun pikanäppäintä painetaan</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">kun pikanäppäin vapautetaan</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Pikanäppäin</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Määritä pikanäppäin:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Käynnistä</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">Tiedosto</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Muokkaa</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">Ikkuna</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Ohje</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Barrier versiosi on vanhentunut. Versio &lt;b&gt;%1&lt;/b&gt; on nyt saatavilla &lt;a href=&quot;%2&quot;&gt;ladattavaksi&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Ohjelmaa ei voida käynnistää</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Ajotiedoston&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;käynnistys ei onnistunut, vaikka se on olemassa. Tarkista onko sinulla riittävät käyttöoikeudet ohjelman ajamiseen.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Barriern asiakasohjelmaa ei löydy</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Ajotiedosto Barriern asiakasohjelmalle puuttuu.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Isäntänimi on tyhjä</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Täytä isäntänimellä johon Barrier voi yhdistää.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Asetustiedostoa ei voida luoda.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Väliaikaista Barriern vaatimaa asetustiedostoa ei voida luoda.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Asetustiedoston tiedostonimi virheellinen </translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Et ole määrittänyt kelvollista asetustiedostoa Barrier palvelimelle. Haluatko määrittää asetustiedoston sijainnin nyt?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrier palvelinta ei löydy</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Ajotiedosto Barrier palvelimelle puuttuu.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier suljettiin virheen vuoksi</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier suljettiin odottamattomasti paluukoodilla %1.&lt;br&gt;&lt;br&gt;Tarkista lokitiedosto saadaksesi lisätietoja.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Pysäytä</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier käynnistyy.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier on käynnissä.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier ei ole käynnissä.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Tuntematon</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Määritä Barriern asetustiedosto</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Tallenna asetukset nimellä...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Tallennus epäonnistui</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Ei voitu tallentaa asetustiedostoa.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Näytön nimi:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">Palvelimen IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Käynnistä</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Käytä olemassa olevaa kokoonpanoa:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Asetustiedosto:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Selaa...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Määritä interaktiivisesti:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Määritä palvelin...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Valmis</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Loki</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Käytä</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP-osoitteet:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Tietoa Barrierstä</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Lopeta</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Lopeta</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Suorita</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Pysäytä</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Pysäytä</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Näytä tila</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">Piilota</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Piilota</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">Näytä</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Näytä</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Tallenna asetukset nimellä...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Tallenna interaktiivisesti määritetty palvelinkokoonpano tiedostoon.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Asetukset</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Muokkaa asetuksia</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Ohjattu asetusten määritys</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Nimetön</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Määritä Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier Configuration (*.sgc);;Kaikki tiedostot (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier Configuration (*.conf);;Kaikki tiedostot (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Ilmoitusalue ei ole käytettävissä, suljetaan.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Näytön nimi puuttuu</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Nimimerkki puuttuu. Ole hyvä ja syötä nimi tai sulje dialogi.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Nimimerkki on sala kuin alias</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Nimimerkki ei voi olla sama kuin alias. Oleva hyvä ja poista alias tai muuta nimimerkki.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Näytön asetukset</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Näytön nimi:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Aliakset</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Lisää</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Poista</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Valintanäppäimet</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Ei mitään</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Kuolleet kulmat</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Ylävasen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Yläoikea</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Alavasen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Alaoikea</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Kulman koko:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Korjaukset</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Korjaa CAPS LOCK näppäin</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Korjaa NUM LOCK näppäin</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Korjaa SCROLL LOCK näppäin</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Korjaa XTest Xineramalle</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Näyttö: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Kaksoisnapsauta muokataksesi asetuksia&lt;br&gt;Vedä näyttö roskakoriin poistaaksesi sen.</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Palvelimen asetukset</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Näytöt ja linkitys</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Vedä näyttö ruudukosta roskakoriin poistaaksesi sen.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Määritä Barrier-palvelimen näyttöasetukset</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Vedä tämä kuvake ruudukkoon lisätäksesi uuden näytön.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Lisää uusia näyttöjä ruudukkoon tai siirrä olemassa olevia vetämällä.
+Vedä näyttö roskakoriin poistaaksesi sen.
+Kaksoisnapsauta näyttöä muokataksesi sen asetuksia.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Pikanäppäimet</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Pikanäppäimet</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Uusi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Muokkaa</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Poista</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Toiminnot</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Uusi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Muokkaa</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Poista</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Palvelimen lisäasetukset</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Vaihda</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Vaihtoviive</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Vaihtoviive kaksoisnapautettaessa</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Asetukset</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Asiakkaiden tarkistusväli</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Suhteutetut kursorin liikkeet</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Synkronoi näytönsäästäjät</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Älä siirrä päällimmäiseksi ikkunaksi Windows palvelimilla</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Kuolleet kulmat</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Ylävasen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Yläoikea</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Alavasen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Alaoikea</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Kulman koko:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Lokitiedoston tallennuspaikka...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Korota Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Oletko varma, että haluat korottaa Barriern?
+Tämä mahdollistaa Barriern käyttämisen korotetuissa prosesseissa ja UAC-dialogeissa, mutta voi aiheuttaa ongelma korottamattomissa prosesseissa. Korota Barrier vain jos se on välttämätöntä.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Asetukset</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Näytön nimi:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Portti:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Sovitin:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Loki</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Lokitaso</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Lokitiedosto</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Selaa...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Virhe</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Kieli:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">Sekalaiset</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Varoitus</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Huomautus</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Tiedot</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Debug</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Debug1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Määritä Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Tee valinta.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Ole hyvä ja syötä sähköpostiosoitteesi ja salasanasi.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Määritä Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Tervetuloa</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Kiitos, että asensit Barriern!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barriern avulla voit helposti jakaa hiiren ja näppäimistösi usean tietokoneen välillä, ja se on ilmainen ja avointa lähdekoodia. Liikuta hiiresi vain näytön reunan yli toiselle koneelle. Voit jopa jakaa leikepöytäsi. Tarvitset vain internet yhteyden. Barrier on cross-platform ( toimii Windows, Mac OS X ja Linuxissa).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Palvelin vai Asiakas?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Tuntematon</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Kirjautuminen epäonnistui, sähköposti tai salasana on väärä.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Kirjautuinen epäonnistui, tapahtui virhe.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Kirjautuinen epäonnistui, tapahtui virhe.
+Palvelimen vaste:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_fr.qm b/src/gui/res/lang/gui_fr.qm
new file mode 100644
index 0000000..8251f4c
--- /dev/null
+++ b/src/gui/res/lang/gui_fr.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_fr.ts b/src/gui/res/lang/gui_fr.ts
new file mode 100644
index 0000000..8d96c5b
--- /dev/null
+++ b/src/gui/res/lang/gui_fr.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="fr" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">À propos de Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Inconnu</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Version:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Configurer l'action</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Choisissez l'action à effectuer</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Appuyez sur une touche</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Relâchez une touche</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Pressez et relâchez une touche</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">uniquement sur ces écrans</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Basculer vers l'écran</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Basculer dans la direction</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">gauche</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">droite</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">haut</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">bas</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Limiter le curseur à l'écran</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">activer</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">activé</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">inactif</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Cette action est exécutée quand</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">Le raccourci est utilisé</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">La touche de raccourci est relachée.</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Raccourci clavier</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Saisir la touche de raccourci&amp;nbsp;:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Démarrer</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Fichier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Editer</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Fenêtre</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Aide</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Votre version de Barrier est périmée. La version &lt;b&gt;%1&lt;/b&gt; est désormais disponible au &lt;a href=&quot;%2&quot;&gt;téléchargement&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Le programme ne peut pas démarrer</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">L'exécutable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;n'a pas pu être correctement lancé, bien qu'il existe. Veuillez vérifier si vos permissions sont suffisantes pour lancer ce programme.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Le client Barrier n'est pas détecté</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">L’exécutable du client Barrier n'existe pas</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Le nom d'hôte est vide</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Veuillez renseigner un nom d'hôte auquel le client Barrier doit se connecter</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Impossible d'écrire le fichier de configuration.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Impossible d'écrire le fichier temporaire de configuration, nécessaire au lancement de barrier.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Le nom du fichier de configuration est incorrect.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Le fichier de configuration du serveur barrier n'est pas correct. Voulez vous visualiser ce fichier ? </translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Le serveur Barrier n'a pas été détecté</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">L'exécutable du serveur Barrier n'existe pas.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier s'est arrêté suite à une erreur</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier s'est terminé de façon inattendue avec le code d'erreur %1.&lt;br&gt;&lt;br&gt; Vous trouverez plus de détails dans le fichier journal.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Stop</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier est en train de démarrer</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier est en cours d'exécution</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier n'est pas en train de s'exécuter</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Inconnu</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Parcourir pour rechercher un fichier de configuration barriers</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Sauvegarder la configuration sous...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Erreur lors de l'enregistrement</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Impossible de sauvegarder la configuration</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Nom de l'écran:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">&amp;Serveur IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Démarrer</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Utiliser la configuration existante : </translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Le fichier de &amp;configuration</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Parcourir...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Configurer interactivement : </translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Configurer le serveur...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Prêt</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Journal</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Appliquer</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">Adresses IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">À &amp;propos de Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">Quitter</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Quitter</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Exécuter</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Arrêt</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Arrêter</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Voir l'état</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">Cac&amp;her</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Cacher</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Montrer</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Montrer</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Sauvegarder la configuration &amp;sous...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Sauvegarder la configuration générée du serveur dans un fichier.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Paramètres</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Éditer les paramètres</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Lancer l'Assistant</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Sans nom</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Installer Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Configuration Barrier (*.sgc);; Tous les fichiers (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier Configurations (*.conf);;All files (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">La barre des tâches n'est pas disponible, fermeture.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Le nom de l'écran est vide</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Le nom de l'écran ne peut pas être vide. Merci de compléter le nom ou d'annuler la boîte de dialogue.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Le nom affiché concorde à l'alias</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Le nom de l'écran ne peut pas être le même que l'alias. Merci de supprimer l'alias ou de changer le nom de l'écran.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Paramètres d'affichage</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Nom de l'écran</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">A&amp;lias</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;Ajouter</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Supprimer</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Touches de combinaison</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Maj&amp;nbsp;:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Maj</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Méta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Aucun</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Coins morts</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Haut-gauche</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Haut-droit</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Bas-gauche</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Bas-droit</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Taille des &amp;coins :</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Correctifs</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Corrige la touche Verrouillage majuscule</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Corrige la touche Verrouillage numérique</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Corrige la touche Verrouillage défilement</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Corrige XTest pour Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Écran: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double cliquer pour modifier les réglages&lt;br&gt;Faire glisser vers la corbeille pour le supprimer</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Configuration Serveur</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Écrans et liens</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Faire glisser un écran de la grille vers la corbeille pour le supprimer.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Configurer l'organisation de votre configuration serveur</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Faire glisser ce bouton sur la grille pour ajouter un nouvel écran</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Faire glisser de nouveaux écrans vers la grille ou déplacer ceux existant.
+Faire glisser un écran dans la corbeille pour le supprimer.
+Double cliquer sur un écran pour modifier ses réglages.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Raccourcis clavier</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Raccourcis clavier</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Nouveau</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Editer</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Supprimer</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">A&amp;ctions</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Nou&amp;veau</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">E&amp;diter</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Suppri&amp;mer</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Configuration serveur avancé</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Basculer</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Changer d'écran après</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Changer d'écran en double-cliquant dans les</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Options</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Vérifier les clients toutes les</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Utiliser des mouvements souris &amp;relatifs</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">S&amp;ynchroniser les écrans de veille</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Ne pas prendre la fenêtre au premier plan sur les serveurs Windows.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Coins morts</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Ha&amp;ut-gauche</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Haut-&amp;droit</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">&amp;Bas-gauche</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Bas-d&amp;roit</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Taille des coi&amp;ns :</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Enregistrer le journal dans le fichier :</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Élever Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Êtes-vous sûr de vouloir élever Barrier&amp;nbsp;?
+Ceci autorise Barrier à interagir avec les processus élevés et le dialogue UAC, mais peut poser des problèmes avec les processus non-élevés. N'élevez Barrier que si cela est nécessaire.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Paramètres</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Nom de l'éc&amp;ran</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">&amp;Port :</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Interface :</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Journal</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Niveau de journalisation :</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Enregistrer le journal dans le fichier&amp;nbsp;:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Parcourir</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Erreur</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">&amp;Langage :</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">Divers</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Avertissement</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Remarque</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Infos</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Debug</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Debug1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Installer Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Choisissez une option s'il vous plait.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Merci d'entrer votre e-mail et votre mot de passe.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Installer Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Bienvenue</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Merci d'avoir installé Barrier !</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier vous permet de partager facilement votre souris et votre clavier entre plusieurs ordinateurs. Il est libre et Open Source. Il suffit de déplacer la souris d'un ordinateur à l'autre en passant par leurs bords, comme pour passer d'un écran à l'autre en multi-écran. Vous pouvez même partager les presse-papiers (copier-coller). Tout ce qu'il faut est une connexion réseau. Barrier est multi-plateforme (fonctionne sur Windows, Mac OS X et Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Serveur ou client ?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Inconnu</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Connexion refusée, e-mail ou mot de passe invalide.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Connexion refusée, une erreur s'est produite.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Connexion refusée, une erreur s'est produite.
+Réponse du serveur :
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_gl.qm b/src/gui/res/lang/gui_gl.qm
new file mode 100644
index 0000000..11d78e1
--- /dev/null
+++ b/src/gui/res/lang/gui_gl.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_gl.ts b/src/gui/res/lang/gui_gl.ts
new file mode 100644
index 0000000..727878f
--- /dev/null
+++ b/src/gui/res/lang/gui_gl.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="gl" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Acerca de Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Descoñecido</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versión:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Configurar acción</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Elixir a acción para realizar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">esquerda</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">dereita</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">arriba</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">abaixo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Ficheiro</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;A súa versión de Barrier está desactualizada. A versión &lt;b&gt;%1&lt;/b&gt; xa está dispoñible para &lt;a href=&quot;%2&quot;&gt;descargar&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Cliente de Barrier non atopado</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Configuración do nome de ficheiro non válida</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Servidor de Barrier non atopado</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier está a iniciarse.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier está a executarse.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier non se está a executar.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Descoñecido</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Erro ao gardar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">&amp;Explorar...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Rexistro</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">Enderezos IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">&amp;Acerca de Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Agochar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Axustes</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Editar axustes</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Executar asistente</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Sen nome</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Axustes</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">&amp;Idioma:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Info</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Benvido/a</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Descoñecido</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_grk.qm b/src/gui/res/lang/gui_grk.qm
new file mode 100644
index 0000000..9793d0d
--- /dev/null
+++ b/src/gui/res/lang/gui_grk.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_grk.ts b/src/gui/res/lang/gui_grk.ts
new file mode 100644
index 0000000..03b71e2
--- /dev/null
+++ b/src/gui/res/lang/gui_grk.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="grk" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Σχετικά με το Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Έκδοση:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">ΟΚ</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">ΔιαμόÏφωσε την ενέÏγεια αυτή</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Επιλέξτε την ενέÏγεια για να εκτελέστεί</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Πιέστε ένα πλήκτÏο άμεσης Ï€Ïόσβασης</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Άφηστε ένα πλήκτÏο άμεσης Ï€Ïόσβασης</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Πιέστε και αφήστε ένα πλήκτÏο άμεσης Ï€Ïόσβασης</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">μόνο σε αυτές τις οθόνες</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Άλλαγή στην οθόνη</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Αλλαγή στην κατεÏθυνση</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">αÏιστεÏά</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">δεξιά</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">πάνω</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">κάτω</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Κλείδωμα δείκτη στην οθόνη</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">Εναλλαγή</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">άνοιξε</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">κλείσε</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Η δÏάση αυτή Ï€Ïαγματοποιείται όταν</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">Το άμεσο πλήκτÏο έχει πατηθεί</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">Το άμεσο πλήκτÏο έχει αφεθεί</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Άμεση Ï€Ïόσβαση</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">ΚαθοÏισμός άμεσου πλήκτÏου</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">ΑÏχείο</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">ΕπεξεÏγασία</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">ΠαÏάθυÏο</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Βοήθεια</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Το Ï€ÏόγÏαμμα δεν μποÏεί να ξεκινήσει</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Έτοιμο</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Έξοδος</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Διακοπή</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Ιδιότητες</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">ΕπεξεÏγασία Ιδιοτήτων</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">ΕπεξεÏγασία</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Ιδιότητες</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">ΠληÏοφοÏίες</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Το Barrier σας αφήνει με εÏκολο Ï„Ïόπο να μοιÏάσετε το ποντίκι και το πληκτÏολόγιο ανάμεσα σε πολλαπλοÏÏ‚ υπολογιστές στο γÏαφείο σας, είναι ΔωÏεάν και Î‘Î½Î¿Î¹Ï‡Ï„Î¿Ï ÎšÏŽÎ´Î¹ÎºÎ±. Απλά μετακινείστε το ποντίκι στην άκÏη της μιας οθόνης στην άλλη. ΜποÏείτε να μοιÏάσετε ακόμα και όλα τα clipboards. Το μόνο που χÏειάζεται μία σÏνδεση δικτÏου. Το Barrier λειτουÏγεί σε πολλαπλές πλατφόÏμες (δουλεÏει σε Windows, Mac OS X και Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_he.qm b/src/gui/res/lang/gui_he.qm
new file mode 100644
index 0000000..2ceeae5
--- /dev/null
+++ b/src/gui/res/lang/gui_he.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_he.ts b/src/gui/res/lang/gui_he.ts
new file mode 100644
index 0000000..192d2a1
--- /dev/null
+++ b/src/gui/res/lang/gui_he.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="he" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">×ודות Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">×œ× ×™×“×•×¢</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">גירס×:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;×ישור</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">הגדר פעולה</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">בחר פעולה לביצוע</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">לחיצה על מקש כלשהו</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">שחרור מקש כלשהו</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">לחיצה ושחרור מקש כלשהו</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">רק ×‘×ž×¡×›×™× ×”×לו</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">עבור למסך</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">החלף בכיוון</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">שמ×ל</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">ימין</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">למעלה</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">למטה</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">נעל ×ת סמן העכבר במסך</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">הדלקה\כיבוי</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">דלוק</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">כיבוי</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">הפעולה מבוצעת ×›×שר</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">המקש לחוץ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">המקש ×ינו לחוץ</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">מקש קיצור</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">הכנס פירוט עבור מקש הקיצור:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">התחל</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;קובץ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;עריכה</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;חלון</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">ע&amp;זרה</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">×œ× × ×™×ª×Ÿ להפעיל ×ת התוכנה</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">היישו×:&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;קיי×, ×ך ×œ× × ×™×ª×Ÿ להפעילו. בדוק ×× ×ž×•×’×“×¨×•×ª עבורך ההר×שות המת×ימות להפעלתו.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">לקוח Barrier ×œ× × ×ž×¦×</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">תוכנת הלקוח של Barrier ×œ× ×§×™×™×ž×ª.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">×œ× ×¦×•×™×Ÿ ×©× ×ž×—×©×‘</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">×× × ×¦×™×™×Ÿ ×©× ×ž×—×©×‘ שלקוח Barrier יתחבר ×ליו.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">×œ× × ×™×ª×Ÿ לכתוב ×ת קובץ ההגדרות</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">×œ× × ×™×ª×Ÿ לכתוב לקובץ ההגדרות הזמני. הדבר הכרחי על מנת להפעיל ×ת Barrier.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">×©× ×§×•×‘×¥ ההגדרות ×ינו חוקי</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">שרת ×”-Barrier ×œ× × ×ž×¦×.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">תוכנת השרת של Barrier ×œ× × ×ž×¦××”.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier נסגרה ×¢× ×©×’×™××”.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier נסגרה ב×ופן ×œ× ×¦×¤×•×™ ×¢× ×©×’×™××” מספר %1.&lt;br&gt;&lt;br&gt;×œ×¤×¨×˜×™× × ×•×¡×¤×™×, פנו לקובץ התיעוד (לוג).</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">עצור</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier מ×תחל</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier רץ ברקע</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier ×œ× ×¨×¥</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">×œ× ×™×“×•×¢</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">מצי×ת קובץ הגדרות של Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">שמור הגדרות &amp;בש×...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">שמירה נכשלה</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">התחל</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">השתמש בהגדרות הקיימות</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">חפש...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">תיעוד פעילות (Log)</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">&amp;שמור שינויי×</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;יצי××”</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">יצי××”</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">הפעל</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">ע&amp;צור</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">עצור</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">הצג &amp;מצב</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">הסתר</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">שמור הגדרות קונפיגורציה כ...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">שמור הגדרות השרת הנוכחיות לקובץ.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">הגדרות</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">שנה הגדרות</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">הפעל ×שף</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">×œ×œ× ×©×</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">הגדרת Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">קבצי הגדרות של Barrier (*.sgc);;All Files (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">קבצי הגדרות של Barrier (*.conf);;All Files (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">מגש המערכת ×œ× ×–×ž×™×Ÿ, יוצ×.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">×œ× ×”×•×§×œ×“ ×©× ×ž×¡×š</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">הגדרות מסך</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">×©× ×ž×¡×š:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">שמות &amp;נוספי×: </translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;הוסף</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">ה&amp;סר</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">לל×</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;פינות מתות</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">עליון-שמ×ל</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">עליון-ימין</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">שמ×לית-תחתונה</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">ימנית-תחתונה</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">&amp;גודל הפינה:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">הגדרות שרת</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">×ž×¡×›×™× ×•×—×™×‘×•×¨×™×</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">גרור ×ת כפתור ×–×” לרשת להוספת מסך.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;חדש</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;עריכה</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">ה&amp;סר</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">פ&amp;עולות</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">חדש</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">כריכה</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">הסרה</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">הגדרות שרת &amp;מתקדמות</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">ה&amp;חלפה</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">החלף ל×חר ×”&amp;מתנה של </translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">×לפיות שניה</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">החלף בלחיצה &amp;כפולה בתוך</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">×&amp;פשרויות</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">בדוק ×ת הלקוח &amp;בכל</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">השתמש בהזזות עכבר &amp;יחסיות</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">סנכרן &amp;שומרי מסך</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;פינות מתות</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">שמ×לית-עליונה</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">ימנית-עליונה</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">שמ×לית-תחתונה</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">ימנית-תחתונה</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">&amp;גודל הפינה:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">הגדרות</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">&amp;×©× ×ž×¡×š:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">&amp;פורט:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">תיעוד פעולה (Log)</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">&amp;רמת תיעוד פעולה (Log)</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">תיעוד לקובץ: </translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">עיון...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">שגי××”</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">×©×™× ×œ×‘</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">הערה</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">מידע</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Debug</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Debug1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">הגדרת Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">×× × ×‘×—×¨ ×ופציה.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">הגדרת Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier מ×פשרת לשתף בקלות ×ת העכבר והמקלדת בין מספר ×ž×—×©×‘×™× ×¢×œ השולחן, וזה חופשית וקוד פתוח. רק להזיז ×ת העכבר מקצה מסך ×חד של המחשב ל×חר. ×תה יכול ×’× ×œ×©×ª×£ ×ת כל לוחות כתיבה שלך. כל מה שצריך ×”×•× ×—×™×‘×•×¨ לרשת. סינרגיה ×”×™× ×—×•×¦×” פלטפורמות (עובד ב-Windows, Mac OS X ו-Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">שרת ×ו לקוח?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">×œ× ×™×“×•×¢</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_hi.qm b/src/gui/res/lang/gui_hi.qm
new file mode 100644
index 0000000..9dad8df
--- /dev/null
+++ b/src/gui/res/lang/gui_hi.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_hi.ts b/src/gui/res/lang/gui_hi.ts
new file mode 100644
index 0000000..a6ba550
--- /dev/null
+++ b/src/gui/res/lang/gui_hi.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="hi" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_hr-HR.qm b/src/gui/res/lang/gui_hr-HR.qm
new file mode 100644
index 0000000..6c3bd19
--- /dev/null
+++ b/src/gui/res/lang/gui_hr-HR.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_hr-HR.ts b/src/gui/res/lang/gui_hr-HR.ts
new file mode 100644
index 0000000..89e437a
--- /dev/null
+++ b/src/gui/res/lang/gui_hr-HR.ts
@@ -0,0 +1,1408 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="hr-HR" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">O Barrieru</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Nepoznato</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">InaÄica:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;U redu</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Podesite radnje</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Odaberite radnju za izvođenje</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Pritisnite tipku preÄaca</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Oslobodite tipku preÄaca</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Pritisnite i oslobodite tipku preÄaca</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">samo na ovim zaslonima</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Prebaci na zaslon</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Prebaci u smjeru</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">lijevo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">desno</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">gore</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">dolje</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">ZakljuÄaj pokazivaÄ na zaslon</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">ukljuÄi/iskljuÄi</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">UkljuÄeno</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">IskljuÄeno</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Ova radnja se izvodi kada</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">je tipka preÄaca pritisnuta</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">je tipka preÄaca osloboÄ‘ena</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Tipka preÄaca</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Odredite tipku preÄaca</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Pokreni</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Datoteka</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Uredi</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Prozor</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Pomoć</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Program ne može biti pokrenut</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Izvršna datoteka&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;ne može se pokrenuti, kao da ne postoji. Provjerite imate li potrebne dozvole za pokretanje ovog programa.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Barrier klijent nije pronađen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Izvršna datoteka barrier klijenta ne postoji.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Naziv raÄunala nedostaje</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">UpiÅ¡ite naziv raÄunala barrier klijenta na koji se spajate.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Nemoguće ispisivanje datoteke postavki</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Privremena datoteka postavki potrebna za pokretanje barriera ne može biti zapisana.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Pogrešan naziv datoteke postavki</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Niste odabrali valjanu datoteku postavki za barrier poslužitelj. Želite li sada odabrati datoteku postavki?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrier poslužitelj nije pronađen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Izvršna datoteka barrier poslužitelja ne postoji.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier je prestao sa radom zbog greške</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier je neoÄekivano prestao sa radom, s izlaznim kodom of %1.&lt;br&gt;&lt;br&gt;Pogledajte izlazni zapis za viÅ¡e pojedinosti.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Zaustavi</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier se pokreće.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier je pokrenut.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier nije pokrenut.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Nepoznato</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Odaberite barrier datoteku postavki</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Spremi postavke kao...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Neuspjelo spremanje</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Nemoguće spremanje postavki u datoteku.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Naziv zaslona:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">&amp;IP poslužitelja:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Pokreni</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Koristi postojeće postavke:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">&amp;Datoteka postavki:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">&amp;Pregledaj...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Podesite interaktivno:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">&amp;Podesite poslužitelj...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Spreman</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Zapis</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">&amp;Primijeni</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP adresa:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">&amp;O Barrieru...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Zatvori</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Zatvori</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Pokreni</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Z&amp;austavi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Zaustavi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">P&amp;rikaži status</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Sakrij</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Sakrij</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Prikaži</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Prikaži</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Spremi postavke &amp;kao...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Spremi interaktivno generirane postavke poslužitelja u datoteku...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Postavke</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Uredi postavke</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Pokeni Äarobnjak</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Neimenovan</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Podesite Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier postavke (*.sgc);;Sve datoteke (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier postavke (*.conf);;Sve datoteke (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Obavijesna ikona je nedostupna, otkazujem.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Naziv zaslona nije upisan</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Postavke zaslona</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Naziv &amp;zaslona:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">N&amp;adimci</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;Dodaj</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Ukloni</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">&amp;Promijeni tipke</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Nijedan</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Mrtvi kutovi</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Gore-lijevo</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Gore-desno</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Dolje-lijevo</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Dolje-desno</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">VeliÄina ku&amp;ta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">&amp;Popraci</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Popravi CAPS LOCK tipku</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Popravi NUM LOCK tipku</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Popravi SCROLL LOCK tipku</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Popravi XTest za Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Zaslon: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Dvostruko kliknite za uređivanje postavki&lt;br&gt;Za uklanjanje zaslona dovucite ga do ikone smeća i ispustite ga</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Postavke poslužitelja</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Zasloni i poveznice</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Za uklanjanje odvucite zaslon iz polja u ikonu smeća.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Podesite izlaz vašeg barrier poslužitelja.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Povucite ovu ikonu zaslona do odabranog polja ispod.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Dovucite novi zaslon u odabrano polje ili premjestite iz trenutnog polja.
+Za brisanje zaslona odvucite ga u ikonu smeća.
+Za uređivanje postavki dvostruko kliknite na zaslon.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Tipke preÄaca</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Tipke preÄaca</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Nova</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Uredi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Ukloni</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">R&amp;adnje</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">No&amp;va</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">U&amp;redi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Uk&amp;loni</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Napredne postavke poslužitelja</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">&amp;UkljuÄi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">UkljuÄi &amp;nakon Äekanja</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">UkljuÄi dvostrukim &amp;dodirom za</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Mogućnosti</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">&amp;Provjeri klijent svakih</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Koristi &amp;relativne pokrete miša</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">U&amp;skladi Äuvare zaslona</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Ne uzimaj &amp;prozor u prednjem planu na Windows poslužiteljima</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Mrtvi kutovi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Go&amp;re-lijevo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Gore-des&amp;no</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">&amp;Dolje-lijevo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Dolje-de&amp;sno</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Vel&amp;iÄina kuta:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Spremi datoteku zapisa u...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">UAC ovlasti Barriera</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Sigurno želite dati UAC ovlasti Barrieru?
+To omogućuje Barrieru interakciju s ovlaštenim procesima i UAC dijalogom, ali može uzrokovati probleme s neovlaštenim procesima. Dajte ovlasti Barrieru samo ako je stvarno potrebno.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Postavke</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Na&amp;ziv zaslona:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">U&amp;laz:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">&amp;SuÄelje:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Prijavljivanje</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">&amp;Razina prijavljivanja:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Prijava u datoteku:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Odaberi...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Greška</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Jezik:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Upozorenje</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Napomena</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Info</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Otklanjanje greške</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Otklanjanje greške1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Otklanjanje greške2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Podesite Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Molim, odaberite mogućnost.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Podesite Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Dobrodošli</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Hvala Å¡to ste instalirali Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier vam omogućuje lagano dijeljenje vaÅ¡g miÅ¡a i tipkovnice izmeÄ‘u viÅ¡e raÄunala na vaÅ¡oj radnoj povrÅ¡ini. Barrier je besplatan i otvorenog kôda. Jednostavno pomaknite vaÅ¡ miÅ¡ preko ruba zaslona jednog raÄunala na zaslon drugog raÄunala. ÄŒak možete dijeliti sve vaÅ¡e meÄ‘uspremnike. Sve Å¡to trebate je Internet veza. Barrier je viÅ¡e platformska aplikacija (radi na Windowsima, Mac OS X i Linuxu).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Poslužitelj ili klijent?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Nepoznato</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_hu-HU.qm b/src/gui/res/lang/gui_hu-HU.qm
new file mode 100644
index 0000000..88499fe
--- /dev/null
+++ b/src/gui/res/lang/gui_hu-HU.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_hu-HU.ts b/src/gui/res/lang/gui_hu-HU.ts
new file mode 100644
index 0000000..524c013
--- /dev/null
+++ b/src/gui/res/lang/gui_hu-HU.ts
@@ -0,0 +1,1407 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="hu-HU" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Barrier névjegy</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Ismeretlen</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Verzió:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Beállítás</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Válasszon egy műveletet</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Nyomjon le egy gyorsbillentyűt</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Engedje fel a gyorsbillentyűt</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Nyomja le, majd engedje fel a gyorsbillentyűt</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">csak ezeken a képernyőkön</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Képernyőváltás</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Irányváltás</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">bal</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">jobb</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">fel</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">le</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Kurzor zárolása a képernyőn</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">váltó</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">BE</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">KI</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Ez a művelet történik amikor</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">a gyorsgomb lenyomva</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">a gyorsgomb felengedve</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Gyorsgomb</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Adjon meg leírást a gyorsgombhoz:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">Fájl</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Szerkeszt</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">Ablak</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Súgó</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">A program nem tud elindulni</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">A végrehajtandó&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;nem indul, esetleg nem létezik. Kérjük ellenőrizze, hogy rendelkezik-e a szükséges engedélyekkel a futtatáshoz.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Barrier kliens nem található</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">A Barrier kliens futtatható állománya nem található.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">A gépnév üres.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Kérjük adja meg a Barrier kliens gép nevét a csatlakozáshoz.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">A konfigurációs fájl nem írható.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Az ideiglenes konfigurációs fájl, amely szükséges a Barrier indításához, nem jött létre.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Konfigurációs fájlnév érvénytelen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Nem adott meg érvényes konfigurációs fájlnevet a barrier szerveren. Meg akarja keresni a konfigurációs fájlt most?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrier szerver nem elérhető</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">A Barrier szerver futtatható állománya nem található.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">A Barrier hibát jelzett, ezért leállt</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">A Barrier a következő hibakóddal lépet ki: %1.&lt;br&gt;&lt;br&gt;A részletekért tekintse meg a naplót.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Stop</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">A Barrier indul.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">A Barrier fut</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">A Barrier nem fut.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Ismeretlen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">A Barrier konfigurációs fájl kijelölése</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Mentse másként a konfigurációt...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Mentés sikertelen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">A konfigurációs fájlt nem lehet menteni.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">A létező konfiguráció használata:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Konfigurációs fájl:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Tallózás...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Interaktív konfiguráció:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Szerver konfiguráció...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Kész</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Napló</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Alkalmaz</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Barrier névjegy...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">Kilépés</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Kilépés</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Fut :-)</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">S&amp;top</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Stop</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">S&amp;how Status</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Konfig mentése mint...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Mentse az interaktívan létrehozott szerver konfigurációt fájlba.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Beállítások</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Beállítások módosítása</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Varázsló futtatása</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Névtelen</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier beállítás</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier konfiguráció (*.sgc);;Minden fájl (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier konfiguráció (*.conf);;Minden fájl (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Rendszer tálca nem elérhető, kilépés.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Képernyőnév üres.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Képernyő beállítások</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Képernyőnév:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Ãlnevek</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Hozzáad</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Eltávolít</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Módosító billentyűk</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">None</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Dead corners</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Bal-felső</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Jobb-felső</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Bal-alsó</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Jobb-alsó</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Sarokméret:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Rögzít</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Rögzíti a CAPS LOCK-ot</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Rögzíti a NUM LOCK-ot</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Rögzíti a SCROLL LOCK-ot</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Fix XTest for Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Képernyő: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Dupla klikk a módosításhoz&lt;br&gt;Húzd a szemetesre a törléshez</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Szerver konfiguráció</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Képernyők és kapcsolatok</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Húzd a képernyőt a listából a szemetesre, hogy eltávolítsd.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Configure the layout of your barrier server configuration.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Húzd ezt a gombot a listába, hogy új képernyőt adj hozzá.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Húzza az új képernyőket a listába vagy mozgassa a meglévőket.
+Húzza a képernyőt a szemetesre, hogy törlődjön.
+Dupla kattintással szerkesztheti a beállításokat.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Gyorsgombok</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Gyorsgombok</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">Új</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Szerkeszt</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Eltávolít</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">A&amp;ctions</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Ne&amp;w</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">E&amp;dit</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Re&amp;move</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">További szerver beállítások</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">&amp;Switch</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Váltás a várakozás után</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Switch on double &amp;tap within</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Beállítások</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Kliensek ellenőrzése</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Relatív egér mozgás használata</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Képernyővédők szinkronizálása</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Don't take &amp;foreground window on Windows servers</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Dead corners</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Bal-felső</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Jobb-felső</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Bal-alsó</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Jobb-alsó</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Sarok méret</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Napló mentése mint...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Beállítások</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Képernyő neve:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">P&amp;ort:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">&amp;Interface:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Naplózás</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Naplózási szint:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Napló fájlba:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Tallózás...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Hiba</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Figyelmeztetés</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Jegyzet</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Infó</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Debug</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Debug1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier beállítás</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Kérem válaszon egy opciót.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier beállítás</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">A Barrier lehetővé teszi a billentyűzet és egér megosztását több számítógép között és ráadásul Ingyenes és nyílt forráskódú. Csak mozgassa az egeret a képernyő szélére, hogy átkerüljön a másik számítógépe monitorára. A számítógépek között a vágólap is megosztásra kerül. Nincs másra szüksége csak hálózati kapcsolatra a két számítógép között. A Barrier operációs rendszer független, így használhatja Windows, Mac OS X és Linux rendszerű számítógéppel.</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Szerver vagy kliens?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Ismeretlen</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_id.qm b/src/gui/res/lang/gui_id.qm
new file mode 100644
index 0000000..2394cf7
--- /dev/null
+++ b/src/gui/res/lang/gui_id.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_id.ts b/src/gui/res/lang/gui_id.ts
new file mode 100644
index 0000000..fc2cd9e
--- /dev/null
+++ b/src/gui/res/lang/gui_id.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="id" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Tentang Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Tak dikenal</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versi:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Oke</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Memulai Barrier.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier tidak berjalan.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Tak dikenal</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Siap</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Laporan</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">Alamat IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Jalankan</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Berhenti</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Berhenti</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Perlihatkan kondisi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Sembunyi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Sembunyi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Simpan konfigurasi sebagai...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Pengaturan</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Ubah pengaturan</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Tanpa nama</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">System tidak memungkinkan, keluar.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Nama layar kosong</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Pengaturan Layar</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Nama Layar:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Nama lain</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Masukan</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Hapus</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Atas-kiri</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Atas-kanan</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Bawah-kiri</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Bawah-kanan</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Konfigurasi server</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">Baru</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Hapus</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Baru</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Ubah</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Hapus</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Pengaturan server lanjutan</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Pengaturan</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Informasi</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier akan mempermudah dalam membagi tetikus dan papan ketik diantara beberapa komputer yang berlainan, dan ini adalah software bebas dan bersumber terbuka. Hanya dengan menggeser tetikus ke pojok layar komputer maka akan berpindah ke layar komputer lainnya. Bahkan kamu dapat membagikan clipboard kamu. Yang kamu butuhkan hanya koneksi jaringan. Synerg adalah program yang dapat berjalan di beberapa Operating System yang berbeda. </translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Tak dikenal</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_is-IS.qm b/src/gui/res/lang/gui_is-IS.qm
new file mode 100644
index 0000000..be651ee
--- /dev/null
+++ b/src/gui/res/lang/gui_is-IS.qm
@@ -0,0 +1 @@
+<¸dÊÍ!¿`¡½Ý \ No newline at end of file
diff --git a/src/gui/res/lang/gui_is-IS.ts b/src/gui/res/lang/gui_is-IS.ts
new file mode 100644
index 0000000..a4c0aa7
--- /dev/null
+++ b/src/gui/res/lang/gui_is-IS.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="is-IS" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_it.qm b/src/gui/res/lang/gui_it.qm
new file mode 100644
index 0000000..6153507
--- /dev/null
+++ b/src/gui/res/lang/gui_it.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_it.ts b/src/gui/res/lang/gui_it.ts
new file mode 100644
index 0000000..a802e28
--- /dev/null
+++ b/src/gui/res/lang/gui_it.ts
@@ -0,0 +1,1408 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="it" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Riguardo a Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Sconosciuto</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versione:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Configura azione</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Scegli l'azione da compiere</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Premi una hotkey</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Rilascia una hotkey</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Premi e rilascia una hotkey</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">solo su questi schermi</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Passa a schermo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Passa in direzione</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">sinistra</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">destra</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">su</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">giu'</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Blocca il cursore sullo schermo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">scambia</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">attiva</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">disattiva</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Questa operazione viene eseguita quando</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">la hotkey viene premuta</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">la hotkey viene rilasciata</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Hotkey</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Specifica la hotkey da utilizzare:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Avvia</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">File</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Modifica</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">Finestra</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Aiuto</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Il programma non puo' essere avviato</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">L'eseguibile &lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;non puo' essere correttamente avviato, anche se esiste. Controlla di avere i permessi necessari ad eseguire il programma (Super User o Administrator?).</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">La componente client di Barrier non è stata individuata</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">L'eseguibile per il client di Barrier non esiste.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Il nome macchina è vuoto</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Si prega di completare il nome macchina per permettere al client Barrier di connettercisi.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Non posso scrivere il file di configurazione</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Il file temporaneo di configurazione necessario ad avviare Barrier non puo' essere scritto.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Il nome file di configurazione non è valido.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Non hai specificato un nome di file di configurazione valido per la componente server di Barrier. Vuoi sfogliare il contenuto del tuo computer per il trovare il file di configurazione ora?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">La componente server di Barrier non è stata trovata</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">L'eseguibile per il server Barrier non esiste.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier si è chiuso con un errore</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier si è chiuso inaspettatamente con un codice di uscita di %1.&lt;br&gt;&lt;br&gt; Sei pregato di visualizzare il log per maggiori dettagli.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Ferma</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier si sta avviando.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier è in funzione.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier non è in funzione.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Sconosciuto</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Sfoglia il contenuto del tuo computer per un file di configurazione di Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Salva la configurazione attuale come...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Salvataggio fallito</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Impossibile salvare la configurazione attuale in un file.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Nome schermo:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">Server IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Avvia</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Utilizza una configurazione esistente:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">File di configurazione:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Sfoglia...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Configurazione interattiva:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Configura il Server...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Pronto</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Eventi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Applica</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">Indirizzi IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Riguardo a Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">Chiudi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Chiudi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Esegui</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Ferma</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Ferma</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Visualizza stato</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">Nascondi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Nascondi</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">Mostra</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Mostra</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Salva configurazione come...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Salva la configurazione interattiva del server in un file.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Impostazioni</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Modifica impostazioni</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Esegui il Wizard</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Senza nome</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configura Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Configurazioni di Barrier (*.sgc);;Tutti i files (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Configurazioni di Barrier (*.conf);;Tutti i files (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">L'icona nella barra di sistema non è disponibile, sto chiudendo.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Il nome dello schermo è vuoto</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Impostazioni dello schermo</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Nome dello schermo:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Soprannomi</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Aggiungi</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Rimuovi</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Tasti speciali</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Nessuno</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Meta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Angoli morti</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">In alto a sinistra</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">In alto a destra</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">In basso a sinistra</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">In basso a destra</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Dimensione dell'angolo</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Punti fissi</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Blocca il tasto CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Blocca il tasto NUM LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Blocca il tasto SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Blocca XTest per Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Schermo: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Doppio click per modificare le impostazioni&lt;br&gt;Trascina lo schermo nel cestino per rimuoverlo</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Configurazione del Server</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Schermi e collegamenti</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Trascina uno schermo dalla griglia al cestino per rimuoverlo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Configura il layout del server Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Trascina questo pulsante sulla griglia per aggiungere un nuovo schermo.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Trascina nuovi schermi sulla griglia o muovi quelli esistenti.
+Trascina uno schermo sul cestino per rimuoverlo.
+Doppio click su uno schermo per modificarne le impostazioni.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Hotkeys</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Hotkeys</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">Nuovo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Modifica</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Rimuovi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Operazioni</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Nuovo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Modifica</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Rimuovi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Configurazioni avanzate del server</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Scambia</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Scambia al termine dell'attesa</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">millisecondi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Scambia con doppio tocco</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Opzioni</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Controlla il client ogni</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Utilizza i movimenti del mouse relativi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Sincronizza gli screen savers</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Non prendere la finestra in primo piano sui servers Windows</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Angoli morti</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">In alto a sinistra</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">In alto a destra</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">In basso a sinistra</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">In basso a destra</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Dimensione dell'angolo:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Salva il file di log...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Eleva Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Sei sicuro di voler elevare Barrier?
+Ciò consentirà a Barrier di interagire con molti processi e con il dialogo UAC,ma potrebbe casusare problemi con i processi non elevati.Eleva Barrier solo se ti serve davvero.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Impostazioni</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Nome dello schermo:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Porta:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Interfaccia:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Registro Eventi</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Livello di registrazione eventi</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Registra gli eventi in un file</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Sfoglia...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Errore</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Lingua</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Avviso</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Nota</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Informazioni</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Debug</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Debug1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configura Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Pregasi selezionare un'opzione</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Inserisci il tuo indirizzo email e la password.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configura Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Ti diamo il benvenuto</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Grazie per aver installato Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier ti permette facilmente di condividere il mouse e la tastiera tra piu' computers sulla tua scrivania, è un software Libero e Gratuito. Ti basta muovere il mouse oltre i bordi dello schermo per passare da un computer ad un altro. Puoi anche condividere la bacheca degli appunti (Copia e Incolla). Tutto cio' che serve è una connessione di rete (WiFi o LAN). Barrier è multi-piattaforma (Funziona correttamente su Windows, Mac OS X e Linux)</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Server o Client?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Sconosciuto</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Login non riuscito, email o password non valida.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_ja-JP.qm b/src/gui/res/lang/gui_ja-JP.qm
new file mode 100644
index 0000000..6310eae
--- /dev/null
+++ b/src/gui/res/lang/gui_ja-JP.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_ja-JP.ts b/src/gui/res/lang/gui_ja-JP.ts
new file mode 100644
index 0000000..c4c0e9e
--- /dev/null
+++ b/src/gui/res/lang/gui_ja-JP.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="ja-JP" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Barrierã«ã¤ã„ã¦</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">ä¸æ˜Ž</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">ãƒãƒ¼ã‚¸ãƒ§ãƒ³:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">OK</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">動作を構æˆ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">実行ã™ã‚‹å‹•ä½œã‚’é¸æŠž</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">ホットキーを押ã™</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">ホットキーを離ã™</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">ホットキーを押ã—ã¦é›¢ã™</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">ã“れらã®ç”»é¢ã ã‘</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">ç”»é¢ã«åˆ‡ã‚Šæ›¿ãˆ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">切り替ãˆã‚‹æ–¹å‘</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">å·¦</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">å³</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">上</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">下</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">カーソルを画é¢ã«é™å®š</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">切り替ãˆ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">オン</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">オフ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">ã“ã®å‹•ä½œã‚’実行ã™ã‚‹æ™‚: </translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">ホットキーを押ã—ãŸã¨ã</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">ホットキーを離ã—ãŸã¨ã</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">ホットキー</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">ホットキーã®æŒ‡å®šæ–¹æ³•ã‚’入力ã—ã¦ãã ã•ã„:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">開始</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">ファイル</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">編集</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">ウィンドウ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">ヘルプ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">プログラムを開始ã§ãã¾ã›ã‚“</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">実行ファイル&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;ã¯å­˜åœ¨ã—ã¾ã™ãŒã€é–‹å§‹ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã“ã®ãƒ—ログラムを動作ã•ã›ã‚‹å分ãªæ¨©é™ãŒã‚ã‚‹ã‹ã©ã†ã‹ç¢ºèªã—ã¦ãã ã•ã„。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">BarrierクライアントãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Barrierクライアントã®å®Ÿè¡Œãƒ•ã‚¡ã‚¤ãƒ«ãŒå­˜åœ¨ã—ã¾ã›ã‚“。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">ホストåãŒå…¥åŠ›ã•ã‚Œã¦ã„ã¾ã›ã‚“</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Barrierクライアントã§æŽ¥ç¶šã™ã‚‹ãƒ›ã‚¹ãƒˆåを入力ã—ã¦ãã ã•ã„。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">構æˆãƒ•ã‚¡ã‚¤ãƒ«ã«æ›¸ãè¾¼ã‚ã¾ã›ã‚“</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Barrierã®é–‹å§‹ã«å¿…è¦ãªä¸€æ™‚çš„ãªæ§‹æˆãƒ•ã‚¡ã‚¤ãƒ«ã‚’書ãè¾¼ã‚ã¾ã›ã‚“。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">構æˆãƒ•ã‚¡ã‚¤ãƒ«åãŒæ­£ã—ãã‚ã‚Šã¾ã›ã‚“。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Barrierサーãƒãƒ¼ã®æ­£ã—ã„構æˆãƒ•ã‚¡ã‚¤ãƒ«ã‚’書ã込んã§ã„ã¾ã›ã‚“。今ã€æ§‹æˆãƒ•ã‚¡ã‚¤ãƒ«ã‚’閲覧ã—ã¾ã™ã‹?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrierサーãƒãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Barrierサーãƒãƒ¼ã®å®Ÿè¡Œãƒ•ã‚¡ã‚¤ãƒ«ãŒå­˜åœ¨ã—ã¾ã›ã‚“。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrierã¯ã‚¨ãƒ©ãƒ¼ã§çµ‚了ã—ã¾ã—ãŸ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrierã¯äºˆæœŸã—ãªã„終了コード%1ã§çµ‚了ã—ã¾ã—ãŸã€‚&lt;br&gt;&lt;br&gt;詳細ã¯ãƒ­ã‚°ã®å‡ºåŠ›ã‚’å‚ç…§ã—ã¦ãã ã•ã„。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">åœæ­¢</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrierを開始中ã§ã™ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrierã¯å‹•ä½œä¸­ã§ã™ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrierã¯å‹•ä½œã—ã¦ã„ã¾ã›ã‚“。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">ä¸æ˜Ž</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Barrierã®è¨­å®šãƒ•ã‚¡ã‚¤ãƒ«ã‚’å‚ç…§</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">設定ã«åå‰ã‚’ã¤ã‘ã¦ä¿å­˜</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">ä¿å­˜ã§ãã¾ã›ã‚“ã§ã—ãŸ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">設定をファイルã«ä¿å­˜ã§ãã¾ã›ã‚“ã§ã—ãŸ</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">ç”»é¢ã®åå‰:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">サーãƒãƒ¼ IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">開始</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">既存ã®è¨­å®šã‚’使用</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">設定ファイル:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">å‚ç…§</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">インタラクティブモードã§è¨­å®š:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">サーãƒãƒ¼ã‚’設定</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">準備完了</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">ログ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">é©ç”¨</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IPアドレス:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Barrierã«ã¤ã„ã¦...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">終了</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">終了</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">実行</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">åœæ­¢</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">åœæ­¢</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">状態を表示</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">éš ã™</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">éš ã™</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">表示ã™ã‚‹</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">表示ã™ã‚‹</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">設定ã«åå‰ã‚’ã¤ã‘ã¦ä¿å­˜</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">インタラクティブモードã§ç”Ÿæˆã—ãŸã‚µãƒ¼ãƒè¨­å®šã‚’ファイルã«ä¿å­˜</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">設定</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">設定を編集</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">ウィザードを実行ã™ã‚‹</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">åå‰ãªã—</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrierã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrierã®æ§‹æˆ(*.sgc);;ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«(*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrierã®æ§‹æˆ(*.conf);;ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«(*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">タスクトレイを利用ã§ãã¾ã›ã‚“。終了ã—ã¾ã™ã€‚</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">ç”»é¢ã®åå‰ãŒç©ºã§ã™ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">ç”»é¢ã®åå‰ã‚’空ã«ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。åå‰ã‚’入力ã™ã‚‹ã‹ãƒ€ã‚¤ã‚¢ãƒ­ã‚°ã‚’キャンセルã—ã¦ãã ã•ã„。</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">ç”»é¢ã®åå‰ã¯åˆ¥åã¨ä¸€è‡´</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">ç”»é¢ã®åå‰ã‚’別åã¨åŒã˜ã«ã™ã‚‹ã“ã¨ã¯å‡ºæ¥ã¾ã›ã‚“。別åを削除ã™ã‚‹ã‹ç”»é¢ã®åå‰ã‚’変更ã—ã¦ãã ã•ã„。</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">ç”»é¢ã®è¨­å®š</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">ç”»é¢ã®åå‰</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">別å</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">追加</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">削除</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">修飾キー</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">シフト</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">シフト</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">コントロール</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">メタ</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">スーパー</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">ãªã—</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;undefinedCtrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">&amp;undefinedl&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">&amp;undefined&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">&amp;undefined&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">無効ã¨ã™ã‚‹è§’</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">左上</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">å³ä¸Š</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">左下</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">å³ä¸‹</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">角ã®å¤§ãã•</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">修正</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">CAPSロックキーを固定</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">NUMロックキーを固定</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">SCROLLロックキーを固定</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Xineramaå‘ã‘ã«XTestを修正</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;ç”»é¢: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;ダブルクリックã§è¨­å®šã‚’編集&lt;br&gt;削除ã™ã‚‹ã¨ãã¯ç”»é¢ã‚’ゴミ箱ã«ãƒ‰ãƒ©ãƒƒã‚°ã—ã¾ã™</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">サーãƒãƒ¼ã®æ§‹æˆ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">ç”»é¢ã¨ãƒªãƒ³ã‚¯</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">削除ã™ã‚‹æ™‚ã¯ã‚°ãƒªãƒƒãƒ‰å†…ã®ç”»é¢ã‚’ゴミ箱ã«ãƒ‰ãƒ©ãƒƒã‚°ã—ã¦ãã ã•ã„。</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">サーãƒæ§‹æˆã®é…置を設定ã™ã‚‹</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">æ–°è¦ç”»é¢ã®è¿½åŠ ã¯ã“ã®ãƒœã‚¿ãƒ³ã‚’グリッド内ã«ãƒ‰ãƒ©ãƒƒã‚°ã—ã¾ã™ã€‚</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">æ–°è¦ç”»é¢ã‚’グリッド内ã«ãƒ‰ãƒ©ãƒƒã‚°ã™ã‚‹ã‹æ—¢å­˜ç”»é¢ã‚’移動ã—ã¦ãã ã•ã„。
+ç”»é¢ã‚’ゴミ箱ã«ãƒ‰ãƒ©ãƒƒã‚°ã™ã‚‹ã¨å‰Šé™¤ã—ã¾ã™ã€‚
+設定を編集ã™ã‚‹å ´åˆã¯ç”»é¢ä¸Šã§ãƒ€ãƒ–ルクリックã—ã¦ãã ã•ã„。</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">ホットキー</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">ホットキー</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">æ–°è¦</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">編集</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">削除</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">アクション</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">æ–°è¦</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">編集</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">削除</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">サーãƒãƒ¼ã®è©³ç´°ãªè¨­å®š</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">切り替ãˆ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">次ã®æ™‚é–“ã®å¾Œåˆ‡ã‚Šæ›¿ãˆ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ミリ秒</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">次ã®æ™‚間内ã®ãƒ€ãƒ–ルタップã§åˆ‡ã‚Šæ›¿ãˆ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">オプション</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">クライアント確èªé »åº¦</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">マウスã®ç›¸å¯¾çš„ãªå‹•ãを使用</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">スクリーンセーãƒãƒ¼ã®åŒæœŸ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Windowsサーãƒã§ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚’å‰é¢ã«è¡¨ç¤ºã—ãªã„</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">無効ã¨ã™ã‚‹è§’</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">左上</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">å³ä¸Š</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">左下</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">å³ä¸‹</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">éš…ã®å¤§ãã•:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">ログファイルã®ä¿å­˜å…ˆ</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Barrierã®æ¨©é™æ˜‡æ ¼</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">本当㫠Barrier を昇格ã•ã›ã¦ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ
+ã“ã‚Œã«ã‚ˆã‚Šæ˜‡æ ¼ã•ã‚ŒãŸãƒ—ロセスや UAC dialog ã¨ã€Barrier ã¨ãŒäº’ã„ã«ä½œç”¨ã—ã‚ã†ã“ã¨ãŒã§ãるよã†ã«ãªã‚‹åé¢ã€æ˜‡æ ¼ã•ã‚Œã¦ã„ãªã„プロセスã¨ã®é–“ã§å•é¡Œã‚’生ã˜ã‚‹ã“ã¨ã‚‚ã‚ã‚Šå¾—ã¾ã™ã€‚確ã‹ã«å¿…è¦ã§ã‚ã‚‹ã¨åˆ¤æ–­ã§ãã‚‹å ´åˆã«ã®ã¿ Barrier ã®æ˜‡æ ¼ã‚’è¡Œã£ã¦ãã ã•ã„。</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">設定</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">スクリーンå:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">ãƒãƒ¼ãƒˆ:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">インターフェース:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">ログ</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">ログレベル:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">ログ記録先ファイル:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">å‚ç…§...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">エラー</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">言語</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">ãã®ä»–</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">警告</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">通知</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">情報</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">デãƒãƒƒã‚°æƒ…å ±</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">デãƒãƒƒã‚°æƒ…å ±1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">デãƒãƒƒã‚°æƒ…å ±2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrierã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">オプションをé¸æŠžã—ã¦ãã ã•ã„。</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">メールアドレスã¨ãƒ‘スワードを入力ã—ã¦ãã ã•ã„。</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrierã®ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">よã†ã“ã</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Barrierをインストールã—ã¦ã„ãŸã ãã€ã‚ã‚ŠãŒã¨ã†ã”ã–ã„ã¾ã™ï¼</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrierã¯è¤‡æ•°ã®ã‚³ãƒ³ãƒ”ュータ間ã®ãƒžã‚¦ã‚¹ã¨ã‚­ãƒ¼ãƒœãƒ¼ãƒ‰ã‚’ç°¡å˜ã«å…±æœ‰ã™ã‚‹ã“ã¨ãŒã§ãるフリーã®ã‚ªãƒ¼ãƒ—ンソースソフトウェアã§ã™ã€‚ã‚るコンピュータã®ç”»é¢ã®ç«¯ã«ãƒžã‚¦ã‚¹ã‚’移動ã™ã‚‹ã¨åˆ¥ã®ã‚³ãƒ³ãƒ”ュータã®ç”»é¢ã«ç§»ã‚Šã¾ã™ã€‚クリップボードを共有ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚å¿…è¦ãªã®ã¯ ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯æŽ¥ç¶šã ã‘ã§ã™ã€‚ Barrierã¯ã‚¯ãƒ­ã‚¹ãƒ—ラットフォームã§Windows, Mac OS X, Linux上ã§å‹•ä½œã—ã¾ã™ã€‚</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">サーãƒãƒ¼ã¾ãŸã¯ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆ</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">ä¸æ˜Ž</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">ログインã¯å¤±æ•—ã—ã¾ã—ãŸã€‚メールアドレスã¾ãŸã¯ãƒ‘スワードãŒç„¡åŠ¹ã§ã™ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">エラーãŒç™ºç”Ÿã—ã€ãƒ­ã‚°ã‚¤ãƒ³ãŒå¤±æ•—ã—ã¾ã—ãŸã€‚
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">エラーãŒç™ºç”Ÿã—ã€ãƒ­ã‚°ã‚¤ãƒ³ãŒå¤±æ•—ã—ã¾ã—ãŸã€‚
+サーãƒã®å¿œç­”:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_ko.qm b/src/gui/res/lang/gui_ko.qm
new file mode 100644
index 0000000..d29f456
--- /dev/null
+++ b/src/gui/res/lang/gui_ko.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_ko.ts b/src/gui/res/lang/gui_ko.ts
new file mode 100644
index 0000000..fee6276
--- /dev/null
+++ b/src/gui/res/lang/gui_ko.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="ko" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Barrierì— ëŒ€í•˜ì—¬</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">알수없ìŒ</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">버전:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">확ì¸(&amp;O)</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">ë™ìž‘ 설정</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">수행할 ë™ìž‘ì„ ì„ íƒí•˜ì„¸ìš”</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">단축키를 누르세요</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">단축키를 놓으세요</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">단축키를 눌렀다 놓으세요</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">ì´ í™”ë©´ì—서만</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">ì´ í™”ë©´ìœ¼ë¡œ 전환</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">ì´ ë°©í–¥ìœ¼ë¡œ 전환</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">왼쪽</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">오른쪽</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">위</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">아래</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">커서를 í™”ë©´ì•ˆì— ê³ ì •</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">토글</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">켜ì§</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">꺼ì§</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">ì´ ë™ìž‘ì´ ìˆ˜í–‰ ë  ë•Œ:</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">단축키가 눌렸습니다.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">단축키가 놓아졌습니다.</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">단축키</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">단축키로 설정할 키를 누르세요:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">시작(&amp;S)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">파ì¼(&amp;F)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">편집(&amp;E)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">ì°½(&amp;W)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">ë„움ë§(&amp;H)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;사용 ì¤‘ì¸ ì‹œë„ˆì§€ëŠ” 최신 ë²„ì „ì´ ì•„ë‹™ë‹ˆë‹¤. 새 버전(&lt;b&gt;%1&lt;/b&gt;)ì„ &lt;a href=&quot;%2&quot;&gt;다운로드&lt;/a&gt; ë°›ì„ ìˆ˜ 있습니다.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">í”„ë¡œê·¸ëž¨ì„ ì‹œìž‘í•  수 없습니다</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">실행파ì¼&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;ì´(ê°€) 존재하지만 성공ì ìœ¼ë¡œ 실행ë˜ì§€ 못했습니다. ì´ í”„ë¡œê·¸ëž¨ì„ ì‹¤í–‰ì‹œí‚¤ê¸° 위한 충분한 ê¶Œí•œì„ ê°€ì§€ê³  있는지 확ì¸í•´ì£¼ì„¸ìš”.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Barrier í´ë¼ì´ì–¸íŠ¸ë¥¼ ì°¾ì„ ìˆ˜ 없습니다.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Barrier í´ë¼ì´ì–¸íŠ¸ 실행 파ì¼ì´ 존재하지 않습니다.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">í˜¸ìŠ¤íŠ¸ëª…ì´ ë¹„ì–´ìžˆìŠµë‹ˆë‹¤.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">í´ë¼ì´ì–¸íŠ¸ê°€ ì ‘ì†í•  í˜¸ìŠ¤íŠ¸ëª…ì„ ìž…ë ¥í•´ì£¼ì„¸ìš”.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">설정파ì¼ì„ 쓸 수 없습니다.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Barrier를 구ë™í•˜ê¸° 위해 필요한 ìž„ì‹œ 설정 파ì¼ì„ 작성할 수 없습니다.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">설정 íŒŒì¼ ì´ë¦„ì´ ì˜¬ë°”ë¥´ì§€ 않습니다</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Barrier 서버를 실행하기 위한 설정 파ì¼ì´ 제대로 작성ë˜ì–´ 있지 않습니다. 지금 설정 파ì¼ì„ 찾아 보시겠습니까?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrier 서버를 ì°¾ì„ ìˆ˜ 없습니다</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Barrier 서버 실행 파ì¼ì´ 존재하지 않습니다.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrierê°€ 오류로 ì¸í•´ 종료ë˜ì—ˆìŠµë‹ˆë‹¤</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrierê°€ %1ì˜ ì½”ë“œë¡œ 비 ì •ìƒì ìœ¼ë¡œ 종료ë˜ì—ˆìŠµë‹ˆë‹¤.&lt;br&gt;&lt;br&gt;ìžì„¸í•œ ì‚¬í•­ì€ ë¡œê·¸ 출력결과를 확ì¸í•˜ì„¸ìš”</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">중지(&amp;S)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier가 실행 중 입니다.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier가 실행 중 입니다.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrierê°€ 실행 중ì´ì§€ 않습니다.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">알수없ìŒ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">barriers 설정 íŒŒì¼ íƒìƒ‰</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">ì„¤ì •ì„ ë‹¤ë¥¸ ì´ë¦„으로 저장...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">ì €ìž¥ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">설정 ì‚¬í•­ì„ íŒŒì¼ì— 저장할 수 없습니다.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">화면 ì´ë¦„:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">서버 IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">시작(&amp;S)</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">기존 ì„¤ì •ì„ ì‚¬ìš©:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">설정 파ì¼(&amp;C):</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">찾아 보기(&amp;B)...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">ìƒí˜¸ìž‘ìš© 설정:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">서버 설정(&amp;C)</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">준비</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">로그</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">ì ìš©(&amp;A)</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP 주소:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Barrierì— ê´€í•˜ì—¬(&amp;A)...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">종료(&amp;Q)</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">종료</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">실행</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">중지(&amp;T)</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">중지</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">ìƒíƒœ 보기(&amp;H)</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">숨기기</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">숨기기</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">ë³´ì´ê¸°</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">ë³´ì´ê¸°</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">ì„¤ì •ì„ ë‹¤ë¥¸ ì´ë¦„으로 저장(&amp;A)...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">ìƒí˜¸ìž‘용으로 ìƒì„±ëœ 서버 ì„¤ì •ì„ íŒŒì¼ë¡œ 저장하기.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">설정</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">설정 편집</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">마법사 실행</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">ì´ë¦„ì—†ìŒ</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">시너지 설정</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier ì„¤ì •íŒŒì¼ (*.sgc);;모든 íŒŒì¼ (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier ì„¤ì •íŒŒì¼ (*.conf);;모든 íŒŒì¼ (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">시스템 트레ì´ë¥¼ 사용할 수 없어 종료합니다.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">스í¬ë¦° ì´ë¦„ì´ ë¹„ì—ˆìŠµë‹ˆë‹¤</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">스í¬ë¦° ëª…ì€ ë¹„ì›Œë‘˜ 수 없습니다. ì´ë¦„ì„ ìž…ë ¥í•˜ê±°ë‚˜ ëŒ€í™”ì°½ì„ ì·¨ì†Œí•˜ì„¸ìš”.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">별명과 ì¼ì¹˜í•˜ëŠ” 화면 ì´ë¦„</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">화면 ì´ë¦„ì€ ë³„ëª…ê³¼ ë™ì¼í•  수 없습니다. ë³„ëª…ì„ ì œê±°í•˜ê±°ë‚˜ 화면 ì´ë¦„ì„ ë°”ê¿”ì£¼ì„¸ìš”.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">화면 설정</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">화면 ì´ë¦„(&amp;N):</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">별칭(&amp;L)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">추가(&amp;A)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">삭제(&amp;R)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">보조 키(&amp;M)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">메타</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">슈í¼</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">ì—†ìŒ</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">메타(&amp;M):</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">슈í¼(&amp;U):</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">사용하지 않는 모서리(&amp;D)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">ìƒë‹¨ 왼쪽</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">ìƒë‹¨ 오른쪽</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">하단 왼쪽</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">하단 오른쪽</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">모서리 í¬ê¸°(&amp;Z)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">ê³ ì •(&amp;F)</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">CAPS LOCK 키 고정</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">NUM LOCK 키 고정</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">SCROLL LOCK 키 고정</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Xinerama를 위한 XTest 고정</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;화면: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;ì„¤ì •ì„ íŽ¸ì§‘í•˜ë ¤ë©´ ë”블í´ë¦­&lt;br&gt;삭제하려면 휴지통으로 드래그 하세요</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">서버 설정</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">화면과 ë§í¬</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">삭제하려면 í™”ë©´ì„ íœ´ì§€í†µìœ¼ë¡œ 드래그 하세요.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Barrier 서버 구성과 ë ˆì´ì•„ì›ƒì„ ì„¤ì •í•˜ì„¸ìš”.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">새로운 í™”ë©´ì„ ì¶”ê°€í•˜ë ¤ë©´ ì´ ë²„íŠ¼ì„ ê²©ìž ì•ˆìœ¼ë¡œ 드래그 하세요.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">새로운 í™”ë©´ì„ ì¶”ê°€í•˜ë ¤ë©´ ìƒë‹¨ì˜ í™”ë©´ì„ ê²©ìž ì•ˆìœ¼ë¡œ 드래그 하세요.
+í™”ë©´ì„ ì‚­ì œí•˜ë ¤ë©´ 휴지통으로 드래그 하세요.
+화면 ì„¤ì •ì„ íŽ¸ì§‘í•˜ë ¤ë©´ ë”블í´ë¦­ 하세요.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">단축키</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">단축키(&amp;H)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">ìƒì„±(&amp;N)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">편집(&amp;E)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">삭제(&amp;R)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">í–‰ë™(&amp;C)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">ìƒì„±(&amp;W)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">편집(&amp;D)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">삭제(&amp;M)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">고급 설정</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">전환(&amp;S)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">대기 후 전환(&amp;A)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">지정시간 ì•ˆì— ë”블 탭으로 전환(&amp;T)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">옵션(&amp;O)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">지정 시간마다 í´ë¼ì´ì–¸íŠ¸ 확ì¸(&amp;C)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">ìƒëŒ€ì ì¸ 마우스 ì´ë™ 사용(&amp;R)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">스í¬ë¦°ì„¸ì´ë²„ ë™ê¸°í™”(&amp;Y)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">í´ë¼ì´ì–¸íŠ¸ë¡œ ì „í™˜ì‹œí•´ë„ ì„œë²„ì˜ í¬ì»¤ìŠ¤ 방지 (전체화면 작업중 í´ë¼ì´ì–¸íŠ¸ë¡œ ì „í™˜í•´ë„ ì „ì²´í™”ë©´ì´ ìœ ì§€ë˜ë©°, 서버ì—ì„œ 전체화면 중 최소화 후 í´ë¼ì´ì–¸íŠ¸ 전환시 ì „ì²´í™”ë©´ì„ ë°©ì§€)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">사용하지 않는 모서리(&amp;D)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">ìƒë‹¨ 왼쪽(&amp;P)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">ìƒë‹¨ 오른쪽(&amp;H)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">하단 왼쪽(&amp;B)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">하단 오른쪽(&amp;G)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">모서리 í¬ê¸°(&amp;N):</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">로그 íŒŒì¼ ì €ìž¥í•˜ê¸°...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Barrier 승급</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Barrierì˜ ê¶Œí•œì„ ìŠ¹ê¸‰ 하시겠습니까?
+ì´ê²ƒì„ 허용하게 ë˜ë©´ Barrier와 ìŠ¹ê¸‰ëœ í”„ë¡œì„¸ìŠ¤, UAC 대화ìƒìžì™€ ìƒí˜¸ìž‘ìš© í•  수 있지만, 승급ë˜ì§€ ì•Šì€ í”„ë¡œì„¸ìŠ¤ë“¤ê³¼ 문제가 ìƒê¸¸ 수 있습니다. Barrier 권한 ìŠ¹ê¸‰ì€ ë°˜ë“œì‹œ 필요한 경우ì—만 사용하세요.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">설정</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">화면 ì´ë¦„(&amp;R):</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">í¬íŠ¸(&amp;O):</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">ì¸í„°íŽ˜ì´ìŠ¤(&amp;I):</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">로그 기ë¡</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">ê¸°ë¡ ìˆ˜ì¤€(&amp;L):</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">파ì¼ë¡œ 저장:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">찾아보기...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">오류</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">언어:(&amp;l)</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">기타</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">경고</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">알림</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">ì •ë³´</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">디버그</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">디버그1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">디버그2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">시너지 설정</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">ì˜µì…˜ì„ ì„ íƒí•˜ì„¸ìš”.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">ì´ë©”ì¼ ì£¼ì†Œ ë° ì•”í˜¸ë¥¼ 입력하세요.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">시너지 설정</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">환ì˜í•©ë‹ˆë‹¤.</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">시너지를 설치하여 주셔서 ê°ì‚¬í•©ë‹ˆë‹¤.</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier는 마우스와 키보드를 여러 ì»´í“¨í„°ì— ì‰½ê²Œ 공유하여 사용할 수 있게 해주는 무료 오픈 소스 프로그램입니다. 마우스를 한쪽 ì»´í“¨í„°ì˜ í™”ë©´ ë으로 옮기기만 하면 다른 ì»´í“¨í„°ì˜ í™”ë©´ìœ¼ë¡œ ì´ë™í•  수 있으며 í´ë¦½ë³´ë“œì˜ ë‚´ìš©ê¹Œì§€ë„ ê³µìœ í•  수 있습니다. 필요한 ê²ƒì€ ë‹¨ì§€ ë„¤íŠ¸ì›Œí¬ ì—°ê²° ë¿ì´ë©° Barrier는 여러 플랫í¼(Windows와 Mac OS X, Linux)ì—ì„œ 사용할 수 있습니다. </translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">서버 ë˜ëŠ” í´ë¼ì´ì–¸íŠ¸?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">알수없ìŒ</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">로그ì¸ì— 실패했습니다. ì „ìžë©”ì¼ ë˜ëŠ” 암호가 잘못ë˜ì—ˆìŠµë‹ˆë‹¤.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">로그ì¸ì— 실패했습니다. 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">로그ì¸ì— 실패했습니다. 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤.
+서버 ì‘ë‹µì€ ë‹¤ìŒê³¼ 같습니다:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_lt.qm b/src/gui/res/lang/gui_lt.qm
new file mode 100644
index 0000000..10a7a05
--- /dev/null
+++ b/src/gui/res/lang/gui_lt.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_lt.ts b/src/gui/res/lang/gui_lt.ts
new file mode 100644
index 0000000..45713fb
--- /dev/null
+++ b/src/gui/res/lang/gui_lt.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="lt" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Gerai</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">kairÄ—</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">dešinė</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">viršus</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">apaÄia</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">ijungta</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">IÅ¡iungta</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">PradÄ—ti</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Pagalba</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Stabdyti</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">Serverio IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">PradÄ—ti</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP adresas:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">Rodyti</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Rodyti</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Klaida</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Įspėjimas</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Informacija</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier leis jums dalintis jūsų pelė ir klaviatūra tarp kompiuterių ir tai nieko nekainuoja ir tai Open Source. Tiesiog perkelkite pelę iš vieno kompiuterio ekrano krašto į kitą. Netgi galite dalintis Clipbourdų. Viskas ko jums reikia, yra tinklo sujungimas. Barrier yra cross-platform dirba ant Windows, Mac OS X ir Linux.</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_lv.qm b/src/gui/res/lang/gui_lv.qm
new file mode 100644
index 0000000..908d8c4
--- /dev/null
+++ b/src/gui/res/lang/gui_lv.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_lv.ts b/src/gui/res/lang/gui_lv.ts
new file mode 100644
index 0000000..e53cf7d
--- /dev/null
+++ b/src/gui/res/lang/gui_lv.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="lv" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">InformÄcija</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier ļauj tev viegli izmantot savu peli un klaviatÅ«ru vairÄkos datoros vienlaicÄ«gi, un tÄ ir bezmakas un atvÄ“rtÄ koda programma. VienkÄrÅ¡i virzi kursoru no viena datora ekrÄna malas uz cita datora ekrÄnu. ArÄ« starpliktuve var bÅ«t kopÄ«ga. VienÄ«gais, kas tev nepiecieÅ¡ams, ir tÄ«kla savienojums. Barrier darbojas uz dažÄdÄm operÄ“tÄjsistÄ“mÄm – Windows, OS X un Linux.</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_mr.qm b/src/gui/res/lang/gui_mr.qm
new file mode 100644
index 0000000..6b0c47d
--- /dev/null
+++ b/src/gui/res/lang/gui_mr.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_mr.ts b/src/gui/res/lang/gui_mr.ts
new file mode 100644
index 0000000..028005d
--- /dev/null
+++ b/src/gui/res/lang/gui_mr.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="mr" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">सिनरà¥à¤œà¥€ बदà¥à¤¦à¤²</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">आवृतà¥à¤¤à¥€:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;ठीक</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">कृती निवडा</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">हॉटकी दाबा</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">हॉटकी सो़डा</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">हॉटकी दाबून सोडा</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">केवळ या पडदà¥à¤¯à¤¾à¤‚वरच</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">पडदà¥à¤¯à¤¾à¤•à¤¡à¥‡ वळा</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">दिशेला वळा</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">डावे</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">उजवे</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">वर</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">खाली</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">करà¥à¤¸à¤° पडदà¥à¤¯à¤¾à¤¶à¥€ बदà¥à¤§ करा </translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">टॉगल</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">चालू</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">बंद</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">ही कृती केली जाते जेवà¥à¤¹à¤¾</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">हॉटकी दाबली गेली आहे</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">हॉटकी सोडणà¥à¤¯à¤¾à¤¤ आली आहे</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">हॉटकी</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">हॉटकीचे तपशील पà¥à¤°à¤µà¤¾:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;फाईल</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;बदल</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;खिडकी</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;मदत</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">कारà¥à¤¯à¤•à¥à¤°à¤® सà¥à¤°à¥‚ होऊ शकला नाही.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;बदल</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">&amp;भाषा:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">माहिती</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">सà¥à¤µà¤¾à¤—तमà¥</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">सिनरà¥à¤œà¥€ इनà¥à¤¸à¥à¤Ÿà¥‰à¤² केलà¥à¤¯à¤¾à¤¬à¤¦à¥à¤¦à¤² धनà¥à¤¯à¤µà¤¾à¤¦.</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">सिनरà¥à¤œà¥€ सह तà¥à¤®à¥à¤¹à¥€ तà¥à¤®à¤šà¤¾ कीबोरà¥à¤¡ आणि माऊस अनेक संगणकांसमवेत वापरू शकता. à¤à¤•à¤¾ पडदà¥à¤¯à¤¾à¤šà¥à¤¯à¤¾ कडेतून माऊस दà¥à¤¸à¤±à¥à¤¯à¤¾ पडदà¥à¤¯à¤¾à¤µà¤° नेऊ शकता, सामाईक कà¥à¤²à¤¿à¤ªà¤¬à¥‹à¤°à¥à¤¡ वारू शकता. आवशà¥à¤¯à¤•à¤¤à¤¾ आहे ती केवळ à¤à¤•à¤¾ नेटवरà¥à¤• कनेकà¥à¤¶à¤¨à¤šà¥€. सिनरà¥à¤œà¥€ मोफत आहे, मà¥à¤•à¥à¤¤ सà¥à¤°à¥‹à¤¤ आहे आणि अनेक पà¥à¤°à¤£à¤¾à¤²à¥à¤¯à¤¾à¤‚वर चालू शकते. (विंडोज, मॅक ओ-à¤à¤¸ à¤à¤•à¥à¤¸ आणि लिनकà¥à¤¸)</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_nl-NL.qm b/src/gui/res/lang/gui_nl-NL.qm
new file mode 100644
index 0000000..b467c66
--- /dev/null
+++ b/src/gui/res/lang/gui_nl-NL.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_nl-NL.ts b/src/gui/res/lang/gui_nl-NL.ts
new file mode 100644
index 0000000..2eabfbb
--- /dev/null
+++ b/src/gui/res/lang/gui_nl-NL.ts
@@ -0,0 +1,1410 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="nl-NL" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Over Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Onbekend</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versie:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Configureer Actie</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Kies de uit te voeren actie</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Druk op een sneltoets</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Laat een sneltoets los</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Druk op een sneltoets en laat los</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">alleen op deze schermen</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Naar scherm gaan</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Verander in een bepaalde richting</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">links</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">rechts</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">omhoog</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">omlaag</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Vergrendel muis op scherm</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">wissel</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">aan</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">uit</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Deze actie wordt uitgevoerd wanneer</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">de sneltoets is ingedrukt</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">de sneltoets is losgelaten</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Sneltoets</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Voer de specificatie voor de sneltoets in:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Bestand</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Bewerken</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Venster</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Help</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt; Uw versie van Barrier is verouderd. Versie &lt;b&gt;%1&lt;/b&gt; is nu beschikbaar om te &lt;a href=&quot;%2&quot;&gt;downloaden&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Het programma kan niet worden gestart</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">De uitvoering van &lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;kon niet succesvol worden uitgevoerd, hoewel het bestaat. Verifieer eerst of u de nodige rechten heeft om het programma uit te voeren.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">De Barrier client is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">De executable voor de Barrier client bestaat niet.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Hostnaam is leeg</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Vul a.u.b. een hostnaam in voor de Barrier client om mee te verbinden.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Kan configuratiebestand niet aanmaken</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Het tijdelijke configuratiebestand om Barrier te starten kan niet geschreven worden.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">De bestandsnaam van de configuratie is ongeldig</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">U heeft een ongeldig configuratiebestand ingegeven voor de Barrier server. Wilt u nu een configuratiebestand opgeven?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrier server is niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">De executable voor de Barrier client bestaat niet.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier werd afgesloten met een error</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier werd onverwachts afgesloten met de volgende exit code: %1.&lt;br&gt;&lt;br&gt;. Raadpleeg de log voor meer details.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Stop</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier wordt gestart.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier is actief.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier is niet actief.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Onbekend</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Open een Barrier configuratiebestand</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Sla configuratie op als...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Opslaan is mislukt</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Kon configuratie niet opslaan naar bestand.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Schermnaam:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">Server IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Gebruik bestaande configuratie:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">&amp;Configuratiebestand:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">&amp;Bladeren...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Configureer grafisch:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">&amp;Configureer Server...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Klaar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Log</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Toep&amp;assen</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP adres:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">&amp;Over Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Sluiten</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Sluit</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Start</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">S&amp;top</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Stop</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">T&amp;oon Status</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Verberg</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Verberg</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Toon</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Toon</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Configuratie opslaan &amp;als...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Sla de grafisch gemaakte serverconfiguratie naar een bestand op.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Instellingen</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Wijzig instellingen</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Start Wizard</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Naamloos</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier configureren</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier Configuratie (*.sgc);;Alle bestanden (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier configuratie (*.conf);;Alle bestanden (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Taakbalk is niet beschikbaar, Barrier wordt afgesloten.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Schermnaam is leeg</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">De schermnaam kan niet leeg zijn. Geef een naam in of annuleer het venster.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Schermnaam komt overeen met alias</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">De schermnaam kan niet hetzelfde zijn als een alias. Verwijder of de alias of verander de schermnaam.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Scherm Instellingen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Scherm &amp;naam:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">A&amp;liassen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;Nieuw</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Verwijderen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">&amp;Prefixtoetsen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Geen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Dode hoeken</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Linksboven</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Rechtsboven</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Linksonder</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Rechtsonder</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">&amp;Grootte:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">&amp;Fixes</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Fix CAPS LOCK toets</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Fix NUM LOCK toets</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Fix SCROLL LOCK toets</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Fix XTest voor Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Scherm: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Dubbelklik om instellingen te wijzigen&lt;br&gt;Sleep een scherm naar de prullenbak om het te verwijderen</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Server configuratie</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Schermen en links</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Sleep een scherm naar de prullenbak om het te verwijderen.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Configureer de layout voor de server configuratie.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Sleep dit icoon naar het raster om een nieuw scherm toe te voegen.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Sleep nieuwe schermen naar het raster of verplaats bestaande schermen.
+Sleep een scherm naar de prullenbak om het te verwijderen.
+Dubbelklik op een scherm om zijn instellingen aan te passen.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Sneltoetsen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Sneltoetsen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Nieuw</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Bewerken</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Verwijderen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">A&amp;cties</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Nieu&amp;w</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Aanpa&amp;ssen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Verwijderen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Geavanceerde server instellingen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Wissel</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Verander na wachten</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Verander na een dubbele klik in</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Opties</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">&amp;Controleer clients elke</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Gebruik &amp;relatieve muis bewegingen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">S&amp;ynchronizeer schermbeveiliging</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Wordt geen actief venster op Windows Servers</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Dode hoeken</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Linksboven</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Rechtsboven</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Linksonder</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Rechtsonder</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Hoek Grootte:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Schrijf logbestand naar...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Verhoog Barrier's Privileges</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Weet je zeker dat je Barrier's privileges wil verhogen? Dit staat interactie toe met verhoogde processen en het UAC venster, maar kan problemen veroorzaken met niet-verhoogde processen. Verhoog Barrier alleen als het noodzakelijk is.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Instellingen</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Scherm naam:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Poort:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">&amp;Interface:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Loggen</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Log niveau:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Log naar bestand:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">&amp;Bladeren...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Fout</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Taal:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">Overige</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Waarschuwing</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Opmerking</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Info</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Debug</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Debug1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier configureren</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Selecteer alstublieft een optie.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Vul uw e-mailadres en wachtwoord in.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier configureren</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Welkom</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Bedankt voor het installeren van Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Met Barrier kunt u eenvoudig uw muis en toetsenbord delen tussen meerdere computers op uw bureau. Bovendien is het gratis en Open Source. Beweeg uw muis over de rand van het scherm van de ene computer naar de andere. U kunt zelfs al uw klemborden delen. Het enige wat u nodig hebt is een netwerkverbinding. Barrier is cross-platform (werkt op Windows, Mac OS X en Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Server of Client?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Onbekend</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Inloggen mislukt, ongeldige gebruikersnaam of wachtwoord.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Inloggen mislukt, er is een fout opgetreden.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Inloggen mislukt, er is een fout opgetreden.
+Foutmelding:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_no.qm b/src/gui/res/lang/gui_no.qm
new file mode 100644
index 0000000..6ebf434
--- /dev/null
+++ b/src/gui/res/lang/gui_no.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_no.ts b/src/gui/res/lang/gui_no.ts
new file mode 100644
index 0000000..42d5b95
--- /dev/null
+++ b/src/gui/res/lang/gui_no.ts
@@ -0,0 +1,1412 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="no" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Om Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Ukjent</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versjon:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Konfigurer handlingen</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Velg handling å utføre</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Trykk en hurtigtast</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Slipp en hurtigtast</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Trykk og slipp en hurtigtast</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">kun på disse skjermene</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Bytt til skjerm</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Bytt i retning</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">venstre</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">høyre</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">opp</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">ned</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">LÃ¥s peker til skjerm</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">veksle</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">på</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">av</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Denne handlingen blir utført når</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">hurtigtasten trykkes</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">hurtigtasten slippes</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Hurtigtast</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Angi spesifikasjon for hurtigtast:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Fil</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Rediger</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Vindu</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Hjelp</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Din Barrier-versjon er utdatert. Versjon &lt;b&gt;%1&lt;/b&gt; er nå tilgjengelig for &lt;a href=&quot;%2&quot;&gt;nedlasting&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Programmet kan ikke starte</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Programfilen &lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt; kunne ikke startes riktig, selv om den finnes. Sjekk at du har tilstrekkelige rettigheter til å kjøre dette programmet.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Finner ikke Barrier klient</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Programfilen for Barrier-klienten finnes ikke</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Vertsnavn er tomt</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Vennligst fyll inn et vertsnavn for Barrier-klienten å koble til.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Kan ikke skrive konfigurasjonsfil</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Den midlertidige oppsettsfilen, som er nødvendig for å starte Barrier, kan ikke opprettes.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Konfigurasjons-filnavn ugyldig</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Du har ikke lagt inn en gyldig oppsettsfil for barrier-tjeneren. Vil du velge oppsettsfilen nå?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Finner ikke Barrier server</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Programfilen for barrier-tjeneren finnes ikke.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier stoppet med en feil</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier stoppet uventet med kode %1.&lt;br&gt;&lt;br&gt;Se i loggen for detaljer.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Stopp</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier starter</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier kjører</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier kjører ikke</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Ukjent</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Velg en oppsettsfil for barriers.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Lagre oppsettet som ...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Kunne ikke lagre</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Kunne ikke lagre oppsettsfilen.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Skjermnavn:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">&amp;Tjener-IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Bruk eksisterende oppsett:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">&amp;Oppsettsfil:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">&amp;Velg...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Sett opp interaktivt:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">&amp;Sett opp tjener...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Klar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Logg</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">&amp;Bruk</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP-adresser:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">&amp;Om Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Avslutt</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Avslutt</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Kjør</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">S&amp;topp</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Stopp</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">&amp;Vis Status</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Skjul</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Skjul</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Vis</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Vis</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">L&amp;agre oppsettet som ...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Lagre det interaktivt opprettede tjener-oppsettet til en fil.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Innstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Endre innstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Kjør Veiledning</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Uten navn</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Konfigurer Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier-oppsett (*.sgc);;Alle filer (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier-oppsett (*.conf);;Alle filer (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Systemstatusfeltet er utilgjengelig - avslutter.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Skjermnavnet er tomt</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Skjermnavnet kan ikke stå tomt. Vennligst fyll inn et navn eller lukk dialogen.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Skjermnavn er likt alias</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Skjermnavnet kan ikke være likt som et alias. Vennligst fjern aliaset eller endre skjermnavnet.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Skjerminnstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Skjerm&amp;navn:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">A&amp;liaser</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;Legg til</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Fjern</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">&amp;Spesialtaster</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Ingen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Døde hjørner</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Øverst til venstre</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Øverst til høyre</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Nederst til venstre</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Nederst til høyre</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Hj&amp;ørnestørrelse</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">&amp;Rettelser</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Fiksér CAPS LOCK tast</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Fiksér NUM LOCK tast</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Fiksér SCROLL LOCK tast</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Fiksér XTest for Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Skjerm: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Dobbeltklikk for å endre innstillinger&lt;br&gt;Dra skjermen til søppelbøtta for å fjerne den</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Tjener-konfigurasjon</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Skjermer og koblinger</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Dra en skjerm fra oppsettet til søppelbøtta for å fjerne den.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Konfigurer oppsettet for din Barrier tjener-konfigurasjon.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Dra denne knappen til oppsettet for å legge til en ny skjerm.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Dra nye skjermer til oppsettet eller flytt rundt på de eksisterende.
+Dra en skjerm til søppelbøtta for å fjerne den.
+Dobbeltklikk en skjerm for å endre dens innstillinger.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Hurtigtaster</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Hurtigtaster</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Ny</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Rediger</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Fjern</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">&amp;Handlinger</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">N&amp;y</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">En&amp;dre</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">&amp;Fjern</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Avanserte tjener-innstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Bytt</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Bytt etter å ha ventet</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Bytt ved dobbel berøring innen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Alternativer</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Kontroller klienter hvert</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Bruk relative musebevegelser</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Synkroniser skjermsparere</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Ikke overta som forgrunnsvindu på Windowstjenere</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Døde hjørner</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Øverst til venstre</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Øverst til høyre</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Nederst til venstre</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Nederst til høyre</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Hjørnestørrelse:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Lagre loggfil til...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Hev Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Er du sikker på at du vil gi Barrier forhøyede rettigheter?
+Dette tillater Barrier å kommunisere med forhøyede prosesser og UAC dialoger. Dette kan føre til problemer med normale prosesser. Bruk dette kun om du må.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Innstillinger</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Skjermnavn</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Port:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Grensesnitt:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Logging</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Loggingnivå:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Logg til fil</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Velg...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Feil</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Språk:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">&amp;Diverse</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Advarsel</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Merknad</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Info</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Feilsøk</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Feilsøk1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Feilsøk2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Konfigurer Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Vennligst velg et alternativ.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Vennligst fyll inn e-postadresse og passord.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Konfigurer Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Velkommen</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Takk for at du installerte Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier kan du enkelt dele dine mus og tastatur mellom flere datamaskiner på skrivebordet, og det er gratis og Open Source. Bare flytte musen utenfor kanten av en datamaskin skjerm til en annen. Du kan også dele alle dine oppslagstavler. Alt du trenger er en nettverksforbindelse. Barrier er cross-platform (fungerer på Windows, Mac OS X og Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Tjener eller Klient?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Ukjent</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Innlogging feilet, ugyldig e-post eller passord.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Innlogging mislykket, en feil oppsto.
+
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Innlogging mislykket, en feil oppsto.
+Svar fra tjener:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_pes-IR.qm b/src/gui/res/lang/gui_pes-IR.qm
new file mode 100644
index 0000000..b900f31
--- /dev/null
+++ b/src/gui/res/lang/gui_pes-IR.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_pes-IR.ts b/src/gui/res/lang/gui_pes-IR.ts
new file mode 100644
index 0000000..d4a0f91
--- /dev/null
+++ b/src/gui/res/lang/gui_pes-IR.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="pes-IR" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">ویراست:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">Ú†Ù¾</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">راست</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">بالا</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">پایین</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">روشن</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">خاموش</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">خروج</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">اجرا</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">توقÙ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">تنظیمات</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">ویرایش تنظیمات</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">بی نام</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">تنظیمات</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_pl-PL.qm b/src/gui/res/lang/gui_pl-PL.qm
new file mode 100644
index 0000000..4b024ad
--- /dev/null
+++ b/src/gui/res/lang/gui_pl-PL.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_pl-PL.ts b/src/gui/res/lang/gui_pl-PL.ts
new file mode 100644
index 0000000..9fef7f3
--- /dev/null
+++ b/src/gui/res/lang/gui_pl-PL.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="pl-PL" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">O Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Nieznane</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Wersja:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Konfiguruj akcje</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Wybierz akcjÄ™ do wykonania</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Wciśnij klawisz</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Zwolnij klawisz</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Wciśnij i zwolnij klawisz</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">tylko na tych ekranach</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Przejdź do ekranu</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Przełącz w kierunku</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">w lewo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">w prawo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">w górę</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">w dół</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Zablokuj kursor myszki dla danego ekranu</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">przełącz</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">wł.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">wył.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Ta akcja jest wykonywana gdy</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">klawisz skrótu jest wciśnięty</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">klawisz skrótu jest zwolniony</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Klawisz skrótu</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Wprowadź opis dla skrótu klawiszowego:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Start</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">Plik</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Edytuj</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">Okno</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Pomoc</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Dostępna jest wersja %1, &lt;a href=&quot;%2&quot;&gt;odwiedź stronę internetową&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Nie można uruchomić programu</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Plik wykonawczy&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;nie został poprawnie uruchomiony, mimo, że istnieje. Proszę sprawdzić czy dane konto posiada odpowiednie uprawnienia do uruchomienia wskazanej aplikacji.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Klient Barrier nie został odnaleziony</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Plik wykonywalny klienta barrier nie istnieje</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Pole &quot;Nazwa hosta&quot; jest puste</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Wypełnij nazwę hosta do której ma się podłączyć klient barrier.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Nie można zapisać pliku konfiguracyjnego</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Nie można zapisać tymczasowego pliku konfiguracyjnego wymaganego do uruchomienia barrier.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Niewłaściwa nazwa pliku konfiguracyjnego</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Nie podałeś poprawnego pliku konfiguracji potrzebnego do uruchomienia serwera barrier. Czy chcesz wskazać ten plik teraz?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Nie znaleziono serwera Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Plik wykonywalny serwera barrier nie istnieje</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier zostało zatrzymane z błędem</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier zakończył działanie w sposób nieoczekiwany, kod wyjścia %1 (exit code). &lt;br&gt;&lt;br&gt;Więcej szczegółów w pliku logów wyjściowych.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Stop</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier jest uruchamiane.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier uruchomione.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier nie uruchomione.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Nieznane</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Wskaż plik konfiguracyjny barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Zachowaj konfigurację jako…</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">BÅ‚Ä…d zapisu</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Nie można zapisać konfiguracji do pliku.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Nazwa ekranu:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">IP &amp;serwera</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Start</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Użyj istniejącej konfiguracji:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">&amp;Plik konfiguracyjny:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Wyszukaj...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Konfiguruj interaktywnie:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Konfiguruj Serwer...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Gotowe</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Log</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">&amp;Zastosuj</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">Adresy IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">O Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">Zakończ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Zakończ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Uruchom</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Zatrzymaj</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Zatrzymaj</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Pokaż status</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Ukryj</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Ukryj</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Pokaż</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Pokaż</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Zapisz konfiguracjÄ™ jako...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Zapisz interaktywnie wygenerowanÄ… konfiguracjÄ™ serwera do pliku.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Ustawienia</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Edytuj ustawienia</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Uruchom Kreatora</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Nienazwany</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Konfiguracja Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Konfiguracje Barrier (*.sgc);;Wszystkie pliki (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Konfiguracje Barrier (*.conf);;Wszystkie pliki (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Tacka systemowa niedostępna, zamykanie…</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Nazwa ekranu jest pusta</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Nazwa ekranu nie może być pusta. Wprowadź nazwę lub anuluj to okno dialogowe.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Nazwa ekranu odpowiada aliasowi</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Nazwa ekranu nie może być taka sama jak alias. Usuń alias lub zmień nazwę ekranu.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Ustawienia ekranu</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Nazwa ekranu:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Aliasy</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Dodaj</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Usuń</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Klawisze &amp;modyfikujÄ…ce</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Żaden</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Narożniki nieczynne</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Lewy-górny</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Prawy-górny</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Lewy-dół</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Prawy-dół</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Wielkość narożników:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Poprawki</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Napraw przycisk CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Napraw przycisk NUM LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Napraw przycisk SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Napraw XTest w Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Ekran: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Kliknij dwukrotnie, aby edytować ustawienia&lt;br&gt;Przeciągnij ekran do kosza, aby go usunąć</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Konfiguracja serwera</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Ekrany i połączenia</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Przeciągnij ekran z siatki do kosza, aby go usunąć.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Konfiguracja układu serwera barrier.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Żeby dodać nowy ekran, przesuń ten przycisk na siatkę.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">PrzeciÄ…gnij nowe ekrany na siatkÄ™ lub przenoÅ› istniejÄ…ce.
+Przeciągnij ekran do kosza, aby go usunąć.
+Kliknij dwukrotnie w ekran, aby edytować jego ustawienia.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Skróty klawiszowe</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Skróty klawiszowe</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Nowy</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Edytuj</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Usuń</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">A&amp;kcje</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">No&amp;wy</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">E&amp;dytuj</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">U&amp;suń</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Zaawansowane ustawienia serwera</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">&amp;Przełącz</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Przełącz &amp;po odczekaniu</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Przełącz po &amp;podwójnym stuknięciu w</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Opcje</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">&amp;Sprawdź klientów co każde</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Użyj &amp;względnych ruchów myszki</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">S&amp;ynchronizuj wygaszacze ekranu</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Nie zmieniaj statusu okna na &amp;wierzch w systemie Windows.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Narożniki nieczynne</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Górny-lewy</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Górny-prawy</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Dolny-lewy</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Dolny-prawy</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Wielkość narożnika:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Zapisz logi do...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Zwiększ uprawnienia Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Czy jesteś pewien, żeby podnieść uprawnienia Barrier?
+Pozwoli to Barrier współpracować z innymi aplikacjami o podniesionych uprawnieniach oraz systemem UAC, ale może stwarzać problemy z aplikacjami bez podniesionych uprawnień. Podnoś uprawnienia tylko w sytuacji, gdy naprawdę tego potrzebujesz.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Ustawienia</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Nazwa ekranu:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Port:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Interfejs:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Logowanie</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Poziom logowania:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Zapisuj logi do pliku:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">PrzeglÄ…daj...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">BÅ‚Ä…d</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Język</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">&amp;Różności</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Ostrzeżenie</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Uwaga</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Info</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Debug</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Debug1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Konfiguracja Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Proszę wybrać opcję,</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Proszę wprowadzić swój adres e-mail i hasło.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Konfiguracja Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Witaj!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Dziękujemy za zainstalowanie Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier pozwala w łatwy sposób dzielić się myszką i klawiaturą pomiędzy wieloma komputerami na Twoim biurku, jest darmowe i Open Source. Wystarczy przesunąć kursor myszy poza krawędź jednego monitora aby przejść na monitor innego komputera. Możesz nawet dzielić wszystkie swoje schowki. Potrzebujesz do tego tylko połączenia z siecią. Barrier jest wieloplatformowe (działa na Windows, Mac OS X i Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Klient czy Serwer?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Nieznane</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Logowanie nie powiodło się - błędny email lub hasło.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Logowanie nie powiodło się, wystąpił błąd.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Logowanie nie powiodło się, wystąpił błąd.
+Odpowiedź serwera:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_pt-BR.qm b/src/gui/res/lang/gui_pt-BR.qm
new file mode 100644
index 0000000..d335d9e
--- /dev/null
+++ b/src/gui/res/lang/gui_pt-BR.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_pt-BR.ts b/src/gui/res/lang/gui_pt-BR.ts
new file mode 100644
index 0000000..802551b
--- /dev/null
+++ b/src/gui/res/lang/gui_pt-BR.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="pt-BR" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Sobre o Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconhecido</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versão:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Configurar Ação</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Escolha a ação a ser executada</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Pressione a tecla de atalho</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Solte a tecla de atalho</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Pressione e solte a tecla de atalho</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">somente nessas telas</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Mude para tela</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Mude de direção</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">esquerda</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">direita</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">acima</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">abaixo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Travar cursor na tela</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">alternar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">ligar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">desligar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Essa ação é executada quando</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">a tecla de atalho é pressionada</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">a tecla de atalho é solta</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Tecla de atalho</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Entre com a descrição da tecla de atalho:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Início</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">Arquivo</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Editar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">Janela</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Ajuda</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;A sua versão do Barrier está desatualizada. Versão &lt;b&gt;%1&lt;/b&gt; está disponível para &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Programa não pode ser iniciado</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">O executável &lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;não pôde ser iniciado com êxito. Por favor, verifique se você tem permissões suficientes para executar este programa.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Cliente Barrier não foi encontrado.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">O executável para o cliente do barrier não existe.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Nome do servidor:</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Por favor, preencha um nome do servidor para o cliente Barrier se conectar.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Não foi possível gravar as configuração</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">O arquivo temporário de configuração, necessário para iniciar o Barrier, não pode ser gravado.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Nome do arquivo de configuração inválido</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Você não preencheu um arquivo de configuração válido para o servidor do Barrier. Você quer procurá-lo agora?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Servidor do Barrier não foi encontrado</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">O executável para o servidor do barrier não existe.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier terminou com um erro</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier fechou inesperadamente retornando o código %1.&lt;br&gt;&lt;br&gt;Por favor veja o log para detalhes.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Parar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier foi iniciado.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier esta rodando.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier não esta rodando.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconhecido</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Selecionar um arquivo de configuração barriers</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Gravar a configuração como...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Gravação falhou</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Não foi possível salvar a configuração no arquivo.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Nome da tela:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">IP do servidor</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Início</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Usar a configuração existente:Use a configuração e</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Arquivo de configuração:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Navegar...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Configurar interativamente:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Configurar servidor</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Pronto</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Registro</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Aplicar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">Endereço ip:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Sobre o Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">Sair</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Sair</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Rodar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Parar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Parar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Mostrar Status</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Ocultar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Ocultar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Exibir</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Exibir</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Gravar a configuração como...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Salvar a configuração do servidor gerada dinamicamente para um arquivo.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Configurações</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Editar configurações</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Executar o assistente</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Sem nome</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Configurações do Barrier (*.sgc);;Todos os arquivos (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Configurações do Barrier (*.conf);;Todos os arquivos (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">A área de notificação não está disponível, saindo.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">O nome de exibição está vazio.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">O nome de exibição não pode ser vazio. Por favor preencha o nome ou cancele.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Nome de exibição corresponde o apelido</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Configurações da tela</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Nome da tela:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Apelidos</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Adicionar</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Remover</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Teclas modificadoras</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Nenhuma</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Meta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Cantos</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Superior-esquerdo</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Superior-direito</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Inferior-esquerdo</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Inferior-direito</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Tamanho do Canto:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Correções</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Trava a tecla CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Trava a tecla CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Trava a tecla SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Corrigir XTest para o Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Tela: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Clique duplo para editar configurações&lt;br&gt;Arraste a tela para a lixeira para remove-la.</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Configuração do servidor</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Telas e links</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Arraste uma tela para a lixeira para remove-la.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Configure a disposição na sua configuração do servidor barrier.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Arraste este botão para o grid para adicionar uma nova tela.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Arrste novas telas para o grid ou mova as existentes.
+Arraste uma tela para a lixeira para remove-la.
+Clique duplo numa tela para editar as suas configurações.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Teclas de Atalho</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Teclas de Atalho</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">Novo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">Editar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Remover</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Ações</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Novo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Editar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Remover</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Configurações avançadas de servidor</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Trocar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Trocar após aguardar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Ligar em toque duplo dentro</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Opções</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Verificar os clientes a cada</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Usar movimentos relativo do mouse</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Sincronize proteções de tela</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Não tome janela de primeiro plano em servidores Windows</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Cantos</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Superior-esquerdo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Superior-direito</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Inferior-esquerdo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Inferior-direito</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Tamanho do Canto:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Salvar o arquivo de log em...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Elevar privilégios de execução do Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Você quer elevar os privilegios de execução do Barrier?
+Isso permite ao Barrier interagir com processos privilegiados e com o UAC, mas pode causar problemas com processos sem privilégios. Eleve os privilégios de execução somente se você realmente precisar.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Configurações</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Nome de Exibição:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Porta:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Interface</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Registrando</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Nível do log:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Salvar log em:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Navegar...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Erro</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">&amp;Idioma:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">Diversos</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Aviso</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Nota</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Info</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Depurar</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Depurar1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Depurar2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Por favor, selecione uma opção</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Por favor informe o seu endereço de e-mail e senha.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Bem-vindo</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Obrigado por instalar o Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier permite que você compartilhe seu mouse e teclado facilmente entre vários computadores na sua área de trabalho, além de ser gratuito e de código aberto. Basta mover o mouse para fora da borda da tela de um computador para outro. Você pode até mesmo compartilhar todas as suas áreas de transferência. Tudo que você precisa é de uma conexão de rede. Barrier é multiplataforma (funciona em Windows, Mac OS X e Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Servidor ou Cliente?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconhecido</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Falha no login, e-mail ou senha inválidos.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Falha no login, ocorreu um erro:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Erro no login, ocorreu um erro.
+Resposta do servidor:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_pt-PT.qm b/src/gui/res/lang/gui_pt-PT.qm
new file mode 100644
index 0000000..cda99e1
--- /dev/null
+++ b/src/gui/res/lang/gui_pt-PT.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_pt-PT.ts b/src/gui/res/lang/gui_pt-PT.ts
new file mode 100644
index 0000000..45a20a8
--- /dev/null
+++ b/src/gui/res/lang/gui_pt-PT.ts
@@ -0,0 +1,1407 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="pt-PT" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Acerca de Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconhecido</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versão</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Definir a ação</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Seleccionar a ação a executar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Pressionar uma tecla de atalho</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Largar uma tecla de atalho</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Pressionar e largar uma tecla de atalho</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">apenas nestes ecrãs</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Mudar para o ecrã</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Mudar na direcção</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">esquerda</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">direita</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">cima</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">baixo</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Manter o cursor no ecrã</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">alternar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">ligado</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">desligado</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Esta ação é executada quando</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">a tecla de atalho é pressionada</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">a tecla de atalho é largada</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Tecla de atalho</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Coloque a descrição para a tecla de atalho</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Iniciar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Ficheiro</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Editar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Janela</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Ajuda</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;A tua versão do Barrier está desatualizada. Versão&lt;b&gt;%1&lt;/b&gt; está agora disponivel &lt;a href=&quot;%2&quot;&gt;para download&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">O programa não consegue iniciar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">O programa&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;, embora exista, não teve êxito a iniciar. Por favor, verifique se tem permissões suficientes para o executar.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">O cliente Barrier não foi encontrado</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">O executável de cliente de Barrier não existe</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">O nome de computador não está preenchido</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Por favor coloque o nome de computador ao qual o cliente Barrier vai ligar-se</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Não foi possivel escrever no ficheiro de configuração</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Não foi possível escrever no ficheiro temporário de configuração de barrier, o qual é indispensável para o iniciar.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Ficheiro de configuração inválido</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Não colocaste um ficheiro de configuração válido para o servidor de barrier. Queres seleccionar o ficheiro de configuração agora?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">O servidor de barrier não foi encontrado</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">O executável de servidor de barrier não existe.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier terminou com erro</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier terminou inesperadamente com o erro %1.&lt;br&gt;&lt;br&gt;Por favor, verifique o registo de log para mais detalhes.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Parar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier está a iniciar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier está em execução</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier não está em execução</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconhecido</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Seleccionar um ficheiro de configuração de barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Guardar a configuração como...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Falhou o registo</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Não foi possível guardar a configuração para o ficheiro.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Nome do ecrã:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">&amp;IP do servidor:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Iniciar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Usar uma configuração guardada:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Ficheiro de &amp;configuração:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">&amp;Explorar...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Configuração interativa:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">&amp;Configurar Servidor...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Pronto</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Registo de eventos</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">&amp;Aplicar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">Endereços IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">&amp;Acerca de Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Sair</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Sair</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Executar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">&amp;Parar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Parar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">M&amp;ostrar Estado</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Esconder</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Esconder</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Mostrar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Mostrar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Guardar a configuração &amp;como...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Guardar a configuração gerada interactivamente para um ficheiro.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Definições</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Alterar definições</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Usar assistente</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Sem nome</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Configuração de barrier (*.sgc);;Todos os ficheiros (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Configuração de barrier (*.conf);;Todos os ficheiros (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">A área de notificação não está disponivel disponivel, saindo.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">O nome do ecrã não foi definido</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">O nome do ecrã não pode estar vazio. Preenche a nome ou fecha a janela.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Configurações de ecrã</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">&amp;Nome do ecrã:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Nomes al&amp;ternativos</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;Adicionar</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Remover</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">&amp;Teclas de modulação</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Nenhum</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">S&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Cantos inacessíveis</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Canto superior esquerdo</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Canto superior direito</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Canto inferior esquerdo</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Canto inferior direito</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Di&amp;mensão do canto:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Co&amp;rrecções</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Corrigir a tecla CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Corrigir a tecla NUM LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Corrigir a tecla SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Corrigir XTest para Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Ecrã: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;duplo clique para alterar as definições&lt;br&gt;Arrastar o ecrã para o lixo para o remover</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Configuração do servidor</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Ecrãs e ligações</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Arrastar um ecrã da grelha para o lixo para o remover.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Configurar a disposição na configuração do servidor de barrier</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Arrastar este botão para a grelha para adicionar um novo ecrã.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Arrastar novos ecrãs para a grelha ou mover os existentes.
+Arrastar um ecrã para o lixo para o remover.
+Faça duplo clique num ecrã para alterar as suas definições.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Teclas de atalho</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Teclas de atalho</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Novo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Editar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Remover</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">&amp;Ações</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">No&amp;vo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">&amp;Alterar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Re&amp;mover</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Definições avançadas do servidor</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">&amp;Alternar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Alternar &amp;após aguardar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Alternar ao tocar duas &amp;vezes em</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Opções</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">&amp;Verificar clientes a cada</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Utilizar movimentos &amp;relativos do rato</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">S&amp;incronizar protectores de ecrã</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Não tirar o &amp;foco da janela activa nos servidores Windows</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Cantos inacessíveis</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Canto su&amp;perior esquerdo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Canto superior &amp;direito</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Canto &amp;inferior esquerdo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Canto inferior direit&amp;o</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Dimensão do Ca&amp;nto:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Guardar ficheiro de registo de eventos para...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Elevar o Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Definições</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Nome do ecrã:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">P&amp;orto:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">&amp;Interface:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Registo de eventos</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">&amp;Nivel de registo:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Ficheiro de registo:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Explorar...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Erro</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">&amp;Linguagem:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Aviso</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Notificação</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Informação</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Depuração</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Depuração1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Depuração2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Por favor selecciona uma opção.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurar Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Bem-vindo(a)</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Obrigado por instalares o Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier permite que uses o teu rato e teclado para controlar vários computadores na tua secretária em simultâneo, é gratuito e Open Source. Basta mover o rato para um lado ou para outro para mudar de ecrã. Podes até copiar e colar de um ecrã para o outro. Precisas apenas de uma ligação de rede. O Barrier é cross-platform (funciona em Windows, Mac OS X e Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Servidor ou Cliente?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Desconhecido</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_ro.qm b/src/gui/res/lang/gui_ro.qm
new file mode 100644
index 0000000..3e71663
--- /dev/null
+++ b/src/gui/res/lang/gui_ro.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_ro.ts b/src/gui/res/lang/gui_ro.ts
new file mode 100644
index 0000000..e79e83b
--- /dev/null
+++ b/src/gui/res/lang/gui_ro.ts
@@ -0,0 +1,1407 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="ro" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Sobre o Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Necunoscut</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versiune:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Confirmă Acțiunea</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Alege acțiunea pentru a o efectua</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Apasă o tastă rapidă</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Eliberează o tastă rapidă</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Apăsaţi şi eliberaţi o tastă rapidă</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">doar pe aceste ecrane</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Comută la ecran</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Comută în direcția</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">stânga</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">dreapta</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">sus</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">jos</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Blochează cursorul la ecran</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">comută</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">Ligado</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">Desligado</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Această acțiune este executată când</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">o tastă rapidă este apăsată</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">o tastă rapidă este eliberată</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished"> Tastă rapidă</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Introdu specificație pentru tasta rapidă:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Fișier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Editează</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Fereastră</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Ajutor</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Programul nu poate fi pornit</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Executabilul&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;nu a putut fi pornit cu succes, deși există. Te rog verifică dacă ai suficiente permisiuni pentru a rula acest program.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Clientul Barrier nu poate fi găsit</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Executabilul pentru clientul barrier nu există.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Numele gazdă este gol</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Te rog să complectezi numele de gazda pentru clientul barrier la care să te conectezi.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Fișierul de configurare nu poate fi scris</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Fișierul de configurare temporar necesar pentru a porni Barrier nu poate fi scris</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Arquivo de configuração inválido.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Nu ati completat un fişier de configurare valabil pentru serverul barrier. Doriţi să răsfoiţi pentru un fişierul de configurare acum?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Clientul Barrier nu poate fi găsit</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Executabilul pentru clientul barrier nu există.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier terminou com um erro</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier a terminat în mod neaşteptat cu un cod %1.&lt;br&gt;&lt;br&gt;Vă rugăm să consultaţi jurnalul pentru detalii.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Stop</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier pornește.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier rulează.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier nu rulează.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Necunoscut</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Răsfoiţi pentru un fişierul de configurare barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Salvează configurația ca...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Salvare eșuată</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Nu sa putut salva configurația în fișier.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Nume de utilizator:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">IP-ul Server-ului:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Folosește configurație existentă:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Fișier de configurare:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Răsfoiește...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Configurare interactivă:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Configurează Server...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Gata</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Log</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Aplică</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">Adrese IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Despre Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Ieșire</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Ieșire</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Pornește</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Stop</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Stop</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Arată Statutul</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">Ascunde </translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Ascunde </translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">Afişează</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Afişează </translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Salvează configurația ca...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Salvaţi configuraţia generată interactiv a serverului într-un fişier.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Setări</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Editează setările</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Pornește Expertul</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Anonim</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurare Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Configurații Barrier (*.sgc);;Toate Fișierele (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Configurații Barrier (*.conf);;Toate Fișierele (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Bara de sistem nu este disponibilă, renunțați.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Numele de ecran este gol.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Setări de ecran</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Nume de ecran.</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Aliasuri</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;Adaugă</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Elimină</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Modificator de taste</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Nici unul</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">&amp;Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Meta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Colțuri moarte</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Stânga-sus</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Dreapta-sus</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Stânga-jos</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Dreapta-jos</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Mărimea colțurilor:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Corecții</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Corectrează tasta CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Corectrează tasta NUM LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Corectrează tasta SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Corectează XTest pentru Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Ecran: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Dublu click pentru editare de setări&lt;br&gt;Glisați ecranul către coșul de gunoi pentru al elimina</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Configurările Serverului</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Ecrane și legături</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Glisați un ecran de pe grilă către coșul de gunoi pentru al elimina.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Configurați aspectul serverului vostru barrier.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Glisați acest buton pe grilă pentru a adăuga un nou ecran.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Glisați ecrane noi pe grilă sau mută cele existente.
+Glisați un ecran către coșul de gunoi pentru al elimina.
+Click dublu pe un ecran pentru ai edita setările.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Taste rapide</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;Taste rapide</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">Nou</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Editează</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;Elimină</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Acțiuni</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Nou</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Editează</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Elimină</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Setari avansate server</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Comută</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Comută după o așteptare de</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Schimbă la atingere dubla în interiorul</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Opțiuni</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Verifică clienți fiecare</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Folosește mișcări relative ale mouse-ului</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Sincronizare protector de ecran</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Nu luaţi fereastre de prim-plan pe servere Windows</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Colțuri moarte</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Stânga-sus</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Dreapta-sus</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Stânga-jos</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Dreapta-jos</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Mărime colțuri</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Salvează fișier jurnal pentru ...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Setări</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Nume ecran:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Port:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Interfață:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Logging</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Nivel logging</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Log în fișier</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Răsfoiește:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Eroare</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Limba:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Avertisment</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Notă</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Informação</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Depanare</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Depanare1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Depanare2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurare Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Te rog selectează o opțiune.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Configurare Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Bine aţi venit</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Va multumim ca a-ti instalat Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier vă permite să partajaţi cu uşurinţă mouse-ul şi tastatura între mai multe computere de pe birou, si este gratuit şi open source. Doar mutaţi mouse-ul de pe marginea ecranului pe un calculator pe altul. Puteţi chiar partaja toate clipboarduri tale. Tot ce trebuie este o conexiune la reţea. Sinergia este cross-platform (funcţionează pe Windows, Mac OS X şi Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Server sau Client?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Necunoscut</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_ru.qm b/src/gui/res/lang/gui_ru.qm
new file mode 100644
index 0000000..8ab5f1a
--- /dev/null
+++ b/src/gui/res/lang/gui_ru.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_ru.ts b/src/gui/res/lang/gui_ru.ts
new file mode 100644
index 0000000..08cc4cb
--- /dev/null
+++ b/src/gui/res/lang/gui_ru.ts
@@ -0,0 +1,1414 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="ru" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">О программе</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">ÐеизвеÑтно</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">ВерÑиÑ:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Ок</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð´ÐµÐ¹ÑтвиÑ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Выберите дейÑтвие Ð´Ð»Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Ðажмите горÑчую клавишу</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">ОтпуÑтите горÑчую клавишу</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Ðажмите и отпуÑтите горÑчую клавишу</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">Только на Ñтих Ñкранах</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">ПереключитьÑÑ Ð½Ð° Ñкран</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">ПереключитьÑÑ Ð² направлении</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">влево</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">вправо</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">Вверх</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">Вниз</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Закрепить курÑор на Ñкран</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">Переключить</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">Включить</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">выкл.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Это дейÑтвие выполнÑетÑÑ ÐºÐ¾Ð³Ð´Ð°</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">горÑÑ‡Ð°Ñ ÐºÐ»Ð°Ð²Ð¸ÑˆÐ° нажата</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">Отпущена горÑÑ‡Ð°Ñ ÐºÐ»Ð°Ð²Ð¸ÑˆÐ°</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">ГорÑÑ‡Ð°Ñ ÐºÐ»Ð°Ð²Ð¸ÑˆÐ°</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Введите опиÑание горÑчей клавиши</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Старт</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Файл</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Редактировать</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Окно</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Помощь</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt; Ваша верÑÐ¸Ñ Barrier уÑтарела. ВерÑÐ¸Ñ &lt;b&gt;%1&lt;/b&gt; доÑтупна Ð´Ð»Ñ &lt;a href=&quot;%2&quot;&gt;ÑкачиваниÑ&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Программа не может быть запущена</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Программа/файл &lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt; Ðе может быть запущена, но она иÑправна. Проверьте - доÑтаточно ли у Ð²Ð°Ñ Ð¿Ñ€Ð°Ð² Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Клиент Barrier не найден</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">ИÑполнÑемый файл Ð´Ð»Ñ ÐºÐ»Ð¸ÐµÐ½Ñ‚Ð° Barrier не найден</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Ð˜Ð¼Ñ Ñ…Ð¾Ñта пуÑто</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">ПожалуйÑта заполните Ð¸Ð¼Ñ Ñ…Ð¾Ñта в клиенте Barrier Ð´Ð»Ñ ÑоединениÑ.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Ðевозможно изменить конфигурационный файл</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Временный файл конфигурации, иÑпользующийÑÑ Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка Barrier, заблокирован Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñи.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° конфигурации неправильное</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Ð’Ñ‹ не указали правильный файл конфигурации Ñервера Barrier. Желаете указать его ÑейчаÑ?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Сервер Barrier не найден</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Ðе найден выполнÑемый файл Ð´Ð»Ñ Ñервера Barrier.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier завершилаÑÑŒ Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¾Ð¹</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier завершилаÑÑŒ неожиданно Ñ ÐºÐ¾Ð´Ð¾Ð¼ выхода %1.&lt;br&gt;&lt;br&gt;ПожалуйÑта проÑмотрите log-файл Ð´Ð»Ñ Ð´ÐµÑ‚Ð°Ð»ÐµÐ¹.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Стоп</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier запуÑкаетÑÑ.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier работает.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier оÑтановлена.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">ÐеизвеÑтно</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Указать файл конфигурации Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Сохранить конфигурацию как...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Сохранение невозможно</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Ðе возможно Ñохранить файл конфигурации.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Ð˜Ð¼Ñ Ñкрана:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">IP Ñервера:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Старт</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">ИÑпользовать ÑущеÑтвующую конфигурацию:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Файл конфигурации:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Обзор...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">ÐвтоматичеÑÐºÐ°Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">ÐаÑтроить Ñервер...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Готово</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Журнал</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">&amp;Применить</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP адреÑа:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">О Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Выход</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Выход</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">ЗапуÑтить</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">ОÑтановить</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">ОÑтановить</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Показать ÑтатуÑ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">Скрыть</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Скрыть</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">Показать</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Показать</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Сохранить конфигурацию как..</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Сохранить автоматичеÑки генерированную конфигурацию в файл.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">ÐаÑтройки</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Изменить наÑтройки</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">ЗапуÑтить МаÑтер</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Без названиÑ</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">ÐаÑтроить Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Barrier (*.sgc);;Ð’Ñе файлы (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Barrier (*.conf);;Ð’Ñе файлы (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">СиÑтемный Ñ‚Ñ€Ñй не доÑтупен, завершение программы.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Ð˜Ð¼Ñ Ñкрана пуÑтое</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Ðазвание не может быть пуÑтым. ПожалуйÑта, либо заполнить Ð¸Ð¼Ñ Ð¸Ð»Ð¸ отменить диалог.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Ð˜Ð¼Ñ Ñкрана:</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Ðазвание Ñкрана не может быть похожим, на ПÑевдоним. ПожалуйÑта удалите ПÑевдоним или поменÑйте Ð˜Ð¼Ñ Ñкрана</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">ÐаÑтройки Ñкрана</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Ð˜Ð¼Ñ Ñкрана:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">ПÑевдонимы</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Добавить</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Удалить</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Клавиши-модификаторы</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Ðет</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Meta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Мертвые углы</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Верхний левый</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Верхний правый</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Ðижний левый</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Ðижний правий</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Размер угла:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">ФикÑирование</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">ФикÑировать CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">ФикÑировать NUM LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">ФикÑировать SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">ФикÑировать XTest Ð´Ð»Ñ Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Экран: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Двойной щелчок Ð´Ð»Ñ Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸&lt;br&gt;ПеретÑните Ñкран в корзину Ð´Ð»Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ñервера</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Экраны и ÑвÑзи</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">ПеретÑните Ñкран Ñ Ñетки в корзину чтобы удалить его.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">ÐаÑтроить раÑположение конфигурации Вашего Ñервера Barrier.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">ПеретÑните Ñту кнопку на Ñетку Ð´Ð»Ñ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ Ñкрана.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">ПеретÑните новый Ñкран на Ñетку или передвиньте ÑущеÑтвующие.
+ПеретÑните Ñкран в корзину чтобы удалить его.
+Двойной щелчок на Ñкране Ð´Ð»Ñ ÐµÐ³Ð¾ конфигурации.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">ГорÑчие клавиши</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">&amp;ГорÑчие клавиши</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Ðовый</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Редактировать</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Удалить</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Де&amp;йÑтвиÑ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Ðо&amp;вый</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Ре&amp;дактировать</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Уда&amp;лить</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Дополнительные наÑтройки Ñервера</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">ПереключитÑÑ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">ПереключитÑÑ Ð¿Ð¾Ñле ожиданиÑ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">мÑек</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">ПереключитÑÑ Ð¿Ð¾ двойному нажатию в течении</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Опции</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">ПроверÑÑ‚ÑŒ клиентов ка&amp;ждые</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">ИÑпользовать &amp;родные наÑтройки курÑора</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">С&amp;инхронизировать заÑтавки</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Ðе держать окно Ñервера поверх вÑех (Ð´Ð»Ñ Windows)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Мертвые углы</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Верхний &amp;левый</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Верхний &amp;правый</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">&amp;Ðижний левый</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Ðижний правы&amp;й</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Раз&amp;мер угла:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Сохранить log-файл в...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">ПовыÑить привилегии Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ (Ð´Ð»Ñ UAC)</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Вы уверены что хотите повышать Barrier?
+Это позволит Barrier взаимодейÑтвовать Ñ Ð¿Ð¾Ð²Ñ‹ÑˆÐ°ÑŽÑ‰Ð¸Ð¼ процеÑÑом и UAC диалогом, но может вызвать проблемы Ñ Ð½ÐµÐ¿Ð¾Ð²Ñ‹ÑˆÐµÐ½Ð½Ñ‹Ð¼Ð¸ процеÑÑами. Повышайте Synregy только еÑли Ñто дейÑтвительно необходимо.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">ÐаÑтройки</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Ð˜Ð¼Ñ Ñк&amp;рана:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">П&amp;орт:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Интер&amp;фейÑ:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Журнал</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Уровень запиÑи в журна?л:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">ЗапиÑывать журнал в файл:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Обзор...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Ошибка</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Язык:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">&amp;Разное</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Предупреждение</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Заметка</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">ИнформациÑ</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Отладка</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Отладка1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Отладка2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">ÐаÑтроить Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">ПожалуйÑта, выберите опцию.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">ПожалуйÑта введите ваш Ð°Ð´Ñ€ÐµÑ Ñлектронной почты и пароль</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">ÐаÑтроить Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Добро пожаловать</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">СпаÑибо за уÑтановку Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier позволÑет вам проÑто и легко работать Ñ Ð¾Ð´Ð½Ð¸Ð¼ набором клавиатуры и мышки между неÑколькими компьютерами. Это беÑплатное приложение Ñ Ð¾Ñ‚ÐºÑ€Ñ‹Ñ‚Ñ‹Ð¼ иÑходным кодом. ПроÑто подведите курÑор за край Ñкрана. Ð’Ñ‹ также можете объединÑÑ‚ÑŒ буферы обмена. Ð’Ñе что вам нужно Ñто Ñетевое подключение. Barrier Ñто кроÑÑ-платформенное приложение, которое работает на Windows, Mac OS X и Linux.</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Сервер или Клиент?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">ÐеизвеÑтно</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">ÐÐµÑƒÐ´Ð°Ñ‡Ð½Ð°Ñ Ð¿Ð¾Ð¿Ñ‹Ñ‚ÐºÐ°, email или пароль введены Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ¾Ð¹</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Войти не удалоÑÑŒ, произошла ошибка.
+
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Войти не удалоÑÑŒ, произошла ошибка.
+
+Ответ Ñервера:
+
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_si.qm b/src/gui/res/lang/gui_si.qm
new file mode 100644
index 0000000..17ba794
--- /dev/null
+++ b/src/gui/res/lang/gui_si.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_si.ts b/src/gui/res/lang/gui_si.ts
new file mode 100644
index 0000000..cb56677
--- /dev/null
+++ b/src/gui/res/lang/gui_si.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="si" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Barrier ගà·à¶± විස්තර</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">වම</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">උඩ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">යට</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">à·€à·à¶©à·ƒà¶§à·„න ඇරඹිය නොහà·à¶š</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">පෙන්වන්න </translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">නිර්නà·à¶¸à·’ක </translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">තිර à·ƒà·à¶šà·ƒà·”ම </translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">විස්තර </translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_sk-SK.qm b/src/gui/res/lang/gui_sk-SK.qm
new file mode 100644
index 0000000..b2abe68
--- /dev/null
+++ b/src/gui/res/lang/gui_sk-SK.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_sk-SK.ts b/src/gui/res/lang/gui_sk-SK.ts
new file mode 100644
index 0000000..aac1379
--- /dev/null
+++ b/src/gui/res/lang/gui_sk-SK.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="sk-SK" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Informácie</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier vám umožní ľahko zdieľaÅ¥ myÅ¡ a klávesnicu medzi viacerými poÄítaÄmi na stole, je to zadarmo a Open Source. Len presunúť kurzor myÅ¡i mimo okraj jedného poÄítaÄa na obrazovke na ÄalÅ¡ie. Môžete dokonca zdieľaÅ¥ vÅ¡etky vaÅ¡e schránok. VÅ¡etko, Äo potrebujete, je pripojenie k sieti. Barrier je cross-platformové (práca na Windows, Mac OS X a Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_sl-SI.qm b/src/gui/res/lang/gui_sl-SI.qm
new file mode 100644
index 0000000..4c5ae79
--- /dev/null
+++ b/src/gui/res/lang/gui_sl-SI.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_sl-SI.ts b/src/gui/res/lang/gui_sl-SI.ts
new file mode 100644
index 0000000..2d4a49d
--- /dev/null
+++ b/src/gui/res/lang/gui_sl-SI.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="sl-SI" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">O Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Neznano</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">RazliÄica:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">V redu</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Neznano</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Informacije</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Debug2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier omogoÄa preprosto souporabo miÅ¡ke in tipkovnice med mnogimi raÄunalniki na vaÅ¡i mizi. Je brezplaÄen in prostokodni program. MiÅ¡kin kazalec lahko preprosto premaknemo preko roba ekrana enega raÄunalnika na ekran drugega. OmogoÄa tudi souporabo odložiÅ¡Äa. Vse kar potrebujete je mrežna povezava. Barrier podpira mnogo operacijskih sistemov (npr. Windows, Mac OS X in Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Neznano</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_sq-AL.qm b/src/gui/res/lang/gui_sq-AL.qm
new file mode 100644
index 0000000..2de4bb7
--- /dev/null
+++ b/src/gui/res/lang/gui_sq-AL.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_sq-AL.ts b/src/gui/res/lang/gui_sq-AL.ts
new file mode 100644
index 0000000..980b795
--- /dev/null
+++ b/src/gui/res/lang/gui_sq-AL.ts
@@ -0,0 +1,1408 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="sq-AL" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Rreth Sunergy</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">E Panjohur</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Versioni:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Veprim Konfigurimi</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Zgjidh veprimin qe do te kryesh</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Shtypni nje tast shpejtesie</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Leshojeni tastin e shpejtesise</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Shtypni dhe leshojeni tastin e shpejtesise</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">Vetem ne keto ekrane</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Kalo ne ekran</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Kalo ne adrese</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">majtas</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">djathtas</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">lart</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">poshte</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Blloko kursorin ne ekran</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">ndrysho gjendje</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">aktiv</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">jo aktiv</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Ky veprim merret kur</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">tasti i shpejtesise eshte i shtypur</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">tasti i shpejtesise eshte leshuar</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Tast shpejtesie</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Vendos specifikimin per tastin e shpejtesise:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Fillo</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Skedar</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Ndrysho</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Dritare</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Ndihme</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Programi nuk mund te filloje</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Skedari i ekzekutueshem &lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;nuk startoi ne menyre te suksesshme, meqe ai nuk egziston. Kontrolloni nese keni te drejta te mjaftueshme per te ekzekutuar kete program.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Klienti nuk u gjet</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Skedari i ekzekutueshem per serverin e barrier nuk egziston.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Emri i hostit eshte bosh</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Ju lutem vendosni nje host te klientit qe deshironi te lidheni.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Nuk mund te shkruaje skedarin e konfiguracionit</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Skedari i perkohshem qe eshte i nevojshem per te startuar Barrier nuk mund te shkruhet.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Emri i skedarit te konfiguracionit i pa sakte</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Nuk keni krijuar nje skedar konfiguracioni per serverin synergu. Deshironi te kerkoni per nje skedar konfiguracioni tani?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Serveri Barrier nuk u gjet</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Skedari i ekzekutueshem per serverin e barrier nuk egziston.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier ndaloi nga nje gabim</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier ndaloi papritur me nje kod gabimi %1.&lt;br&gt;&lt;br&gt; Ju lutem shikoni detajet e te dhenave.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Ndalo</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier po fillon.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier nuk po ekzekutohet.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">E Panjohur</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Kerko per nje skedarin e konfigurimit te Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Ruaj konfiguracionin si...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Ruajtja deshtoi</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Nuk mund te ruaje skedarin e konfiguracionit.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Emri i ekranit:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">&amp;IP e Serverit:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;Fillo</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Perdorni konfiguracionin aktual:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Skedari i konfiguracionit:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Shfleto...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Konfiguracion Interaktiv:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Konfiguro Serverin...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Gati</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Te dhena</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">&amp;Apliko</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP adresa:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Rreth Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Dil</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Dil</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Ekzekuto</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Ndalo</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Ndalo</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Shfaq Statusin</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Fshih</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Fshih</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Shfaq</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Shfaq</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Rruaj konfiguracionin si...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Ruaj konfiguracionit interaktiv te serverit ne nje skedar.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Konfigurime</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Ndrysho konfigurimet</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Ekzekuto Magjistarin</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Paemer</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Vendos Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Konfiguracioni i Barrier (*.sgc);;Te gjithe (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Konfiguracioni i Barrier (*.conf);;Te gjithe (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Ikona e sistemin nuk eshte aktive, duke e mbyllur.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Emri i ekranit eshte bosh</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Konfigurimet e Ekranit</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Emri i ekranit:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Emertimet</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Shto</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Hiq</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Celesat modifikues</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Mbi</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Superior</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Asnje</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Mbi:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Superior:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Kende te vdekura</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Lart-Majtas</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Lart-Djathtas</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Majtas-poshte</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Djtathtas-poshte</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Madhesia e kendit:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Rregullime</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Rregullo tastin CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Rregullo tastin NUM LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Rregullo tastin SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Rregullo XTest per Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Ekran: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Dopjo klik per te ndryshuar konfigurimet&lt;br&gt;Zhvendos ekranit ne kosh per ta fshire</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Konfiguracioni i Serverit</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Ekranet dhe lidhjet</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Kalo ekranit nga rrjeta ne kosh per ta fshire</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Konfiguroni paraqitjen e konfiguraciont te serverit.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Kaloni kete buton ne rrjet per te shtuar nje ekran te ri.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Kaloni ekranet e reja ne rrjet ose levizni ato egzistuesit rreth tij.
+Zhvendos ekranit ne kosh per ta fshire
+Dopjo klik mbi nje ekran per te ndryshuar konfigurimet e tij.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Tastet e shpejtesise</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Tastet e shpejtesise</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">I Ri</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Ndrysho</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Hiq</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Veprim</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">I ri</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Modifiko</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">&amp;Hiq</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Konfigurime te avancuara te serverit</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">&amp;Ndrysho</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Ndrysho pasi te presesh</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Kaloni ne lidhje te dyfishte</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Mundesi</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">&amp;Kontrollo klientet cdo</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Beni levizjet perkatese te mausit</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Sinkronizo mbrojtesit e ekranit</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Mos i jepni prioritet dritareve ne serverat me SO Windows</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Kende te vdekura</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Lart-majtas</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Lart-djathtas</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Majtas-poshte</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Djathtas-poshte</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Madhesia e Kendit:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Rruaj dokumentin e te dhenave ne...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Permireso Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished"> Jeni te sigurte qe doni te permiresoni Barrier?
+Kjo i jep mundesi Barrier te bashkeveproje me procese te larta dhe UAC, por ju mund te shkaktoni probleme me proceset e uleta. Permiresoni Barrier vetem nese ju nevojitet.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Konfigurime</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Emri i ekranit:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Porta:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">&amp;Nderfaqja</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Duke hyre</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">&amp;Niveli i hyrjes</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Hyr ne dokument:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Shfleto...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Gabim</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Lajmerim</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Shenim</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Informacion</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Rregullo</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Rregullo1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Rregullo2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Vendos Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Ju lutem zgjidhni nje opsion.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Vendos Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Sunergy ju mundeson lehtesisht te ndani mausin dhe tastieren me disa komopjutera te tjere ne tavolinen tuaj, eshte FALAS dhe me KOD TE HAPUR. Vetem levizni mausin nga kompjuteri juaj ne nje kompjuter tjeter. Madje mund te beni ndani te gjitha ekranet. Ju duhet vem nje lidhje interneti. Sunergy eshte nje platformi e gjere (punon ne Windows, MAC OS X dhe Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Server apo Klient?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">E Panjohur</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_sr.qm b/src/gui/res/lang/gui_sr.qm
new file mode 100644
index 0000000..ab1fce3
--- /dev/null
+++ b/src/gui/res/lang/gui_sr.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_sr.ts b/src/gui/res/lang/gui_sr.ts
new file mode 100644
index 0000000..f245475
--- /dev/null
+++ b/src/gui/res/lang/gui_sr.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="sr" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Информације</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_sv.qm b/src/gui/res/lang/gui_sv.qm
new file mode 100644
index 0000000..c5fb93b
--- /dev/null
+++ b/src/gui/res/lang/gui_sv.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_sv.ts b/src/gui/res/lang/gui_sv.ts
new file mode 100644
index 0000000..8494970
--- /dev/null
+++ b/src/gui/res/lang/gui_sv.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="sv" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Om Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Okänd</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Version:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Konfigurera</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Välj funktion som skall utföras</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Tryck ned en snabbtangent</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Släpp en snabbtangent</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Tryck ned och släpp en snabbtangent</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">endast på dessa skärmar</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Växla till skärm</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Växla i riktning</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">vänster</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">höger</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">upp</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">ner</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Lås muspekaren till skärm</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">skifta</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">på</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">Av</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Detta utförs när </translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">snabbtangenten trycks in</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">snabbtangenten släpps</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Snabbtangent</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Beskriv snabbtangenten:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Start</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">Arkiv</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Redigera</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">Fönster</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Hjälp</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Du kör inte den senaste versionen av Barrier. Version &lt;b&gt;%1&lt;/b&gt; finns tillgänglig för &lt;a href=&quot;%2&quot;&gt;nedladdning&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Programmet kan inte startas</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Programmet&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;kunde inte startas, men det finns. Var vänlig och kontrollera att du har tillräckliga rättigheter för att köra detta program.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Barrierklienten hittades inte</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Den exekverbara filen för Barrierklienten existerar inte.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Värddatornamnet är inte satt</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Fyll i det värddatornamn som Barrierklienten skall ansluta till.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Kan inte spara konfigurationsfilen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Den tillfälliga konfigurationsfilen som krävs för att kunna starta Barrier kan inte skapas.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Namnet på konfigurationsfilen är inte giltigt</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Du har inte fyllt i en giltig konfigurationsfil för Barrierservern. Vill du bläddra efter konfigurationsfilen nu?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrierservern hittades inte</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Den körbara filen för Barrierservern existerar inte.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier avslutades med ett fel</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrierterminalen avslutades oväntat med avbrottskoden %1.&lt;br&gt;&lt;br&gt;Kontrollera loggdatan för detaljer.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Stopp</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier startas.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier körs.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier körs inte.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Okänd</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Bläddra efter Barriers konfigurationsfil.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Spara konfiguration som...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Sparades inte</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Kunde inte spara konfiguration till fil.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Skärmnamn:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">Serverns IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Start</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Använd befintlig konfiguration:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Konfigurationsfil:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">Bläddra...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Konfigurera interaktivt:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Konfigurera server...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Klar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Logg</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Verkställ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP-adresser:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Om Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">Avsluta</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Avsluta</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Kör</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">S&amp;topp</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Stopp</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Visa Status</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">Dölj</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Dölj</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">Visa</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Visa</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Spara konfiguration som...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Spara den interaktivt genererade serverkonfigurationen till en fil.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Inställningar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Ändra inställningar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Kör guiden</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Namnlös</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Ställ in Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier-konfigurationer (*.sgc);;Alla filer (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier-konfigurationer (*.conf);;Alla filer (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Systemfältet otillgängligt, avslutar.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Skärmnamnet är tomt</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Skärmnamnet kan inte vara tomt. Fyll i ett namn eller avbryt dialogrutan.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Skärmnamn matchar alias</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Skärmnamnet kan inte vara samma som en alias. Var vänlig att antingen ta bort alias eller ändra skärmnamnet.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Skärminställningar</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Skärmnamn:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Alias</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Lägg till</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Ta bort</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Modifieringstangenter</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Ingen</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Meta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Döda hörn</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Över vänster</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Över höger</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Nedre vänstra hörnet</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Nedre höger</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Hörnets storlek:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Korrigeringar</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">Korrigera CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">Korrigera NUM LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">Korrigera SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">Korrigera XTest för Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Skärm:&lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Dubbelklicka för att ändra inställningar&lt;br&gt;Dra skärmen till papperskorgen för att ta bort den</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Serverkonfiguration</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Skärmar och länkar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Dra en skärm från rutnätet till papperskorgen för att ta bort den.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Konfigurera layouten på din Barrier-konfiguration.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Dra den här knappen till rutnätet för att lägga till en ny skärm.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Dra nya skärmar till rutnätet eller flytta runt existerande.
+Dra en skärm till papperskorgen för att ta bort den.
+Dubbelklicka på en skärm för att ändra dess inställningar.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Snabbtangenter</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Snabbtangenter</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">Ny</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Redigera</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Ta bort</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Åtgärder</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Ny</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Ändra</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Ta bort</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Avancerade serverinställningar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Växla</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Växla efter väntan</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Växla eller dubbelklicka inuti</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Alternativ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Kontrollera klienter varje</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">Använd relativa musrörelser</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Synkronisera skärmsläckare</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Använd inte förgrundsfönster på Windowsservrar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Döda hörn</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Övre vänstra hörnet</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Övre högra hörnet</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Nedre vänstra hörnet</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Nedre högra hörnet</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Hörnstorlek:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Spara loggfil till...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Förhöj Barriers behörighet</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">Är du säker på att du vill förhöja Barriers behörighet?
+Detta låter Barrier interagera med förhöjda processer och UAC-dialogen, men kan skapa program med processer som inte är förhöjda. Förhöj Barriers behörighet enbart om du verkligen måste.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Inställningar</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Skärmnamn:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Port:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Gränssnitt:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Loggning</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Loggningsnivå:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Logga till fil:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Bläddra:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Fel</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Språk:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">&amp;Övrigt</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Varning</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Notering</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Info</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Felsök</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Felsök1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Felsök2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Ställ in Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Välj ett alternativ.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Var vänlig skriv din e-postaddress och lösenord.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Ställ in Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Välkommen</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Tack för att du installerar Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Med Barrier kan du enkelt dela din mus och ditt tangentbord med flera datorer på ditt skrivbord, och det är Fri och Öppen mjukvara. För bara muspekaren över kanten på en datorskärm för att den skall dyka upp på nästa. Du kan till och med dela utklipp. Allt du behöver är en nätverksanslutning. Barrier är multiplattform (fungerar på både Windows, Mac OS X och Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Server eller klient?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Okänd</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Inloggning misslyckades, ogiltig e-postaddress eller lösenord.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Inloggning misslyckades, ett problem inträffade.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Inloggning misslyckades, ett problem inträffade.
+Server svar:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_th-TH.qm b/src/gui/res/lang/gui_th-TH.qm
new file mode 100644
index 0000000..3762d16
--- /dev/null
+++ b/src/gui/res/lang/gui_th-TH.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_th-TH.ts b/src/gui/res/lang/gui_th-TH.ts
new file mode 100644
index 0000000..878a4e3
--- /dev/null
+++ b/src/gui/res/lang/gui_th-TH.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="th-TH" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹€à¸£à¸² Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">รุ่น:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;ตà¸à¸¥à¸‡</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">à¸à¸³à¸«à¸™à¸”ค่าà¸à¸²à¸£à¸à¸£à¸°à¸—ำ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">ซ้าย</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">ขวา</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">ขึ้น</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">ลง</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">เปิด</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">ปิด</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;เริ่มต้น</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;ไฟล์</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;à¹à¸à¹‰à¹„ข</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;หน้าต่าง</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;ช่วยเหลือ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;หยุด</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;เริ่มต้น</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">พร้อมà¹à¸¥à¹‰à¸§</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">บันทึà¸à¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;ออà¸</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">ออà¸</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">เปิดทำงาน</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">หยุด</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;ซ่อน</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">ซ๋อน</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;à¹à¸ªà¸”ง</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">à¹à¸ªà¸”ง</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">ตั้งค่า</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¸•à¸±à¹‰à¸‡à¸„่า</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">ยังไม่มีชื่อ</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">ตั้งค่าหน้าจอ</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">&amp;เพิ่ม</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;ลบออà¸</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;à¹à¸à¹‰à¹„ข</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">&amp;ลบออà¸</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">ตั้งค่า</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">ข้อมูล</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier ช่วยให้คุณสามารถà¹à¸šà¹ˆà¸‡à¸›à¸±à¸™à¹€à¸¡à¸²à¸ªà¹Œà¹à¸¥à¸°à¹à¸›à¹‰à¸™à¸žà¸´à¸¡à¸žà¹Œà¸£à¸°à¸«à¸§à¹ˆà¸²à¸‡à¸„อมพิวเตอร์หลายเครื่องบนโต๊ะทำงานของคุณà¹à¸¥à¸°à¸à¹‡à¸Ÿà¸£à¸µà¹à¸¥à¸° Open Source เพียงà¹à¸„่เลื่อนเมาส์ของคุณปิดขอบของหน้าจอคอมพิวเตอร์เครื่องหนึ่งของเมื่อไปยังอีภคุณยังสามารถà¹à¸šà¹ˆà¸‡à¸›à¸±à¸™à¸—ั้งหมดของ clipboards ของคุณ ทั้งหมดที่คุณต้องมีà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¸•à¹ˆà¸­à¹€à¸„รือข่าย Barrier เป็นข้ามà¹à¸žà¸¥à¸•à¸Ÿà¸­à¸£à¹Œà¸¡ (เมื่อใช้งานบน Windows, Mac OS X à¹à¸¥à¸° Linux)</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_tr-TR.qm b/src/gui/res/lang/gui_tr-TR.qm
new file mode 100644
index 0000000..55ef0f8
--- /dev/null
+++ b/src/gui/res/lang/gui_tr-TR.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_tr-TR.ts b/src/gui/res/lang/gui_tr-TR.ts
new file mode 100644
index 0000000..d35de14
--- /dev/null
+++ b/src/gui/res/lang/gui_tr-TR.ts
@@ -0,0 +1,1410 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="tr-TR" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Barrier hakkında</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Bilinmeyen</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Sürüm:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">&amp;Tamam</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">Eylemi yapılandır</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Gerçekleştirilecek eylemi seçin</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">Kısayol tuşuna basın</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">Kısayol tuşunu bırakın</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">Kısayol tuşuna basıp bırakın</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">Sadece bu ekranlarda</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Ekranı değiştir</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Yönü değiştir</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">sol</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">saÄŸ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">yukarı</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">aşağı</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Ä°mleci ekrana sabitle</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">DeÄŸiÅŸtir</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">açık</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">kapalı</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">İşlem gerçekleştiğinde</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">Kısayol tuşu aktif</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">Kısayol tuşu serbest</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">Kısayol</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Kısayol tuşu için tanımlama gir</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;BaÅŸlat</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">&amp;Dosya</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Düzenle</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">&amp;Pencere</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">&amp;Yardım</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Barrier versiyonunuz eski. Versiyon &lt;b&gt;%1&lt;/b&gt; hazır. İndirmek için &lt;a href=&quot;%2&quot;&gt;tıklayın&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Program başlatılamadı</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Çalıştırılabilir&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;mevcut olmasına rağmen başarılı olarak başlatılamadı. Lütfen bu programı çalıştırabilmek için yeterli yetkiye sahip olup olmadığınızı kontrol edin.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Barrier alıcıyı bulamadı</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Barrier için çalışan kullanıcı makine yok.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Anabilgisayar boÅŸ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Lütfen Barrier'nin bağlanması için bir bilgisayar adı girin</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Ayar dosyası yazılamadı</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">Geçici ayar dosyası Barrier'nin başlaması için gerekli, üzerine kaydedilemez</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Geçersiz ayar dosyası adı</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Barrier sunucusu için geçerli bir ayar dosyası oluşturmadınız. Bu ayar dosyasına şimdi göz atmak ister misiniz?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Barrier sunucusu bulunamadı</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Barrier için çalışan sunucu makine yok.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier bir hata yüzünden sonlandırıldı.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier %1 hata kod ile sonlandırıldı.&lt;br&gt;&lt;br&gt;Detaylar için hata günlüğüne bakınız.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">&amp;Dur</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier başlıyor</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier çalışıyor</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier çalışmıyor</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Bilinmeyen</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Barrier ayar dosyasına göz at</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Ayarları kaydet</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">Kaydetme hatası</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Ayarlar kaydedilemedi</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Ekran adı:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">&amp;Sunucu IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">&amp;BaÅŸlat</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">Varolan ayarları kullan</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">&amp;Ayar dosyası:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">&amp;Araştır...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">İnteraktif olarak yapılandır:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">&amp;Sunucu ayarla</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Hazır</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Kayıt kütüğü</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">Uygula</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP adresleri:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Barrier hakkında</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">&amp;Çıkış</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Çıkış</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Çalıştır</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Dur</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Dur</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Durumu Göster</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">&amp;Gizle</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Gizle</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">&amp;Göster</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Göster</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Ayarları farklı kaydet</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">İnteraktif olarak oluşturulmuş olan sununu yapılandırmasını dosyaya kaydet.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Ayarlar</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Ayarları düzenle</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">Sihirbazı Çalıştır</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Ä°simsiz</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier Kurulum</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">Bildirim alanı kullanılamıyor, kapatılıyor.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Ekran ismi boÅŸ</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Ekran ismi boş bırakılamaz. Lütfen bir ad girin yada iptal edin.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Ekran ismiyle takma ad aynı olamaz. Lütfen takma adı silin yada ekran adını değiştirin.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">Ekran Ayarları</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Görünen isim:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">Takma adlar</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Ekle</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Kaldır</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">&amp;DeÄŸiÅŸtirici tuÅŸlar</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Süper</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Yok</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Al&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">M&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Süper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Kör nokta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Sol-üst</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Sağ-üst</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Sol-alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">SaÄŸ-alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Köşe boyutu:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">Düzeltmeler</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">BÃœYÃœK YAZMAYI kapat</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">NUM LOCK kapat</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">SCROLL LOCK kapat</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">XTest için Xinerama yı onar.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Ekran: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Çift tıklayarak ayarları düzenleyin &lt;br&gt;Çöp kutusundan silmek için ekranda sürükleyin</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">Sunucu Yapılandırması</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Ekranlar ve Bağlantıları</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">Izgaradan çıkartmak için çöp kutusuna sürükleyin ve silin.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Lütfen sinerji sunucu yapılandırma düzeninizi yapılandırın.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">Yeni bir ekran eklemek için bu butonu ızgaraya sürükleyin.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">Izgaraya yeni ekranları sürükleyin veya çevresinde mevcut olanları taşıyın.
+Silmek için çöp tenekesine ekranı sürükleyin.
+Kendi ayarlarınızı düzenlemek için bir ekran üzerine çift tıklayın.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">Kısayollar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">Kısayollar</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">&amp;Yeni</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">&amp;Düzenle</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Kaldır</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Eylemler</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Yeni</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Düzen</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Kaldır</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Gelişmiş sunucu ayarları</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">&amp;DeÄŸiÅŸtir</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">ms</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">&amp;Seçenekler</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">Her zaman kullanıcıları denetle</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">&amp;İlişkili mouse harekelerini kullanın</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Ekran koruyucuları eşitle</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Lütfen önplan penceresini Windows sunucularına almayın.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">&amp;Kör nokta</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Ãœst-sol</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Ãœst-SaÄŸ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Alt-sol</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Alt-SaÄŸ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Köşe Boyutu:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Log dosyası kaydet...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Barrieri Yükselt</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Ayarlar</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Ekran adı:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Port:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">Arayüz:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">Log kayıtları</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Kayıt Seviyesi : </translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">Dosyaya kaydet : </translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">Araştır...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Hata</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">&amp;Dil</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">Uyarı</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Not</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Bilgi</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">Hata Ayıkla</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">Hata Ayıkla1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">Hata Ayıkla2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier Kurulum</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Lütfen seçiminizi yapınız.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Lütfen e-posta adresinizi ve şifrenizi girin.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Barrier Kurulum</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">HoÅŸ Geldiniz</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">Sinerji yüklediğiniz için teşekkür ederiz !</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier, aynı mouse ve klavye ile birden çok bilgisayarı kontrol etmenizi sağlayan Özgür ve Açık Kaynak Kodlu bir uygulamadır. Yapmanız gereken mouse imlecinizi bilgisayar ekranından diğerine sürüklemek. Barrier aynı zamanda ortak bir panoya da izin vermektedir yani bilgisayarlar arası kopyala/yapıştır yapmanıza da olanak tanır. Tek gereken bilgisayarlarınız arasında kuruluş olan network bağlantısı. Barrier, çoklu platform desteği de vemektedir (Windows, Linux ve Mac OS X işletimde sistemlerinde çalışır). </translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Sunucu veya Kullanıcı?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Bilinmeyen</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Giriş başarısız, e-posta yada şifre yanlış.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Giriş başarısız, bir hata meydana geldi.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Giriş başarısız, bir hata meydana geldi.
+Sunucu yanıtı:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_uk.qm b/src/gui/res/lang/gui_uk.qm
new file mode 100644
index 0000000..275a454
--- /dev/null
+++ b/src/gui/res/lang/gui_uk.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_uk.ts b/src/gui/res/lang/gui_uk.ts
new file mode 100644
index 0000000..68cac01
--- /dev/null
+++ b/src/gui/res/lang/gui_uk.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="uk" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Про Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Ðевідомо</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">ВерÑÑ–Ñ:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">OK</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ñ–Ð¹</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">Виберіть дію, що виконуєтьÑÑ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">ÐатиÑніть гарÑчу клавішу</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">ВідпуÑÑ‚Ñ–Ñ‚ÑŒ гарÑчу клавішу</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">ÐатиÑніть та відпуÑÑ‚Ñ–Ñ‚ÑŒ гарÑчу клавішу</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">тільки на цих екранах</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">Перехід на екран</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">Перехід в напрÑмку</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">вліво</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">вправо</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">вверх</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">вниз</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">Закріпити курÑор за екраном</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">переключити</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">ввімк.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">вимк.</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Ð¦Ñ Ð´Ñ–Ñ Ð²Ð¸ÐºÐ¾Ð½ÑƒÑ”Ñ‚ÑŒÑÑ ÐºÐ¾Ð»Ð¸</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">гарÑча клавіша натиÑнута</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">гарÑча клавіша відпущена</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">ГарÑча клавіша</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">Вкажіть Ñпецифікацію гарÑчої клавіші:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Старт</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">Файл</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">РедагуваннÑ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">Вікно</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">Допомога</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Ваша верÑÑ–Ñ Barrier заÑтаріла. Зараз доÑтупна верÑÑ–Ñ &lt;b&gt;%1&lt;/b&gt; Ð´Ð»Ñ &lt;a href=&quot;%2&quot;&gt;завантаженнÑ&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Програма не може бути запущена</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">Виконуваний фал &lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;не може запуÑтитиÑÑŒ, хоча він Ñ–Ñнує. Будь лаÑка, перевірте чи у Ð’Ð°Ñ Ð´Ð¾Ñтатньо прав Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑку цієї програми.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">Ðе знайдено жодного клієнта Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Виконуваної програми Ð´Ð»Ñ ÐºÐ»Ñ–Ñ”Ð½Ñ‚Ð° Barrier не Ñ–Ñнує.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">Ім'Ñ Ñ…Ð¾Ñту не вказане</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">Будь лаÑка вкажіть ім'Ñ Ñ…Ð¾Ñту до Ñкого повинен приєднатиÑÑ ÐºÐ»Ñ–Ñ”Ð½Ñ‚ Barrier.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">Ðеможливо здійÑнити Ð·Ð°Ð¿Ð¸Ñ Ñƒ файл конфігурації</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">ТимчаÑовий файл конфігурації, Ñкий необхідний Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑку Barrier заблокований Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">Ðеправильне ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ конфігурації</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">Ви не заповнили правильний файл конфігурації Ð´Ð»Ñ Ñервера Barrier . Бажаєте зробити це зараз?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Ðеможливо знайти Ñервер Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Виконуваної програми Ð´Ð»Ñ Ñерверу Barrier не Ñ–Ñнує.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier завершена з помилками</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrier завершилаÑÑŒ неочікувано з кодом of %1.&lt;br&gt;&lt;br&gt;Будь лаÑка переглÑньте log-запиÑи Ð´Ð»Ñ Ð´ÐµÑ‚Ð°Ð»ÐµÐ¹.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Стоп</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier запущено.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier працює.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier зупинено.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Ðевідомо</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">Вкажіть файл конфігурації Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">Зберегти конфігурацію Ñк...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">ПоÑилка збереженнÑ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">Ðеможливо зберегти файл конфігурації.</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">Ім'Ñ ÐµÐºÑ€Ð°Ð½Ñƒ:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">IP Ñерверу:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Старт</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">ВикориÑтати Ñ–Ñнуючу конфігурацію:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">Файл конфігурації:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">ПереглÑд...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">Ðалаштувати інтерактивно:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">Ðалаштувати Ñервер...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Готово</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">Лог</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">ЗаÑтоÑувати</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP адреÑи:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">Про Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">Закрити</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Закрити</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">ЗапуÑтити</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Зупинити</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">Зупинити</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Показати ÑтатуÑ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">Приховати</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">Приховати</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">Показати</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">Показати</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">Зберегти конфігурацію Ñк...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">Зберегти інтерактивно згенеровану конфігурацію Ñервера у файл.</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">ÐалаштуваннÑ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Редагувати налаштуваннÑ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">ЗапуÑтити Помічника</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">Без назви</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Barrier (*.sgc);;Ð’ÑÑ– файли (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Barrier (*.conf);;Ð’ÑÑ– файли (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">СиÑтемний трей недоÑтупний, здійÑнюєтьÑÑ Ð²Ð¸Ñ…Ñ–Ð´.</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">Ім'Ñ ÐµÐºÑ€Ð°Ð½Ñƒ порожнє</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Ім'Ñ ÐµÐºÑ€Ð°Ð½Ñƒ не може бути порожнім. Будь лаÑка вкажіть ім'Ñ Ð°Ð±Ð¾ ÑкаÑуйте діалог.</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">Ім'Ñ ÐµÐºÑ€Ð°Ð½Ñƒ Ñпівпадає з пÑевдонімом</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Ім'Ñ ÐµÐºÑ€Ð°Ð½Ñƒ не може бути таким Ñамим Ñк пÑевдонім. Будь лаÑка видаліть пÑевдонім або змініть ім'Ñ ÐµÐºÑ€Ð°Ð½Ñƒ</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐµÐºÑ€Ð°Ð½Ñƒ</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">Ім'Ñ ÐµÐºÑ€Ð°Ð½Ñƒ:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">ПÑевдоніми</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Додати</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Видалити</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">Клавіші-модифікатори</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Жодна</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Meta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Мертві кути</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">Верхній лівий</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">Верхній правий</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">Ðижній лівий</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">Ðижній правий</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">Розмір кута:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">ФікÑаціÑ</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">ФікÑувати CAPS LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">ФікÑувати NUM LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">ФікÑувати SCROLL LOCK</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">ФікÑувати XTest Ð´Ð»Ñ Xinerama</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;Екран: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Подвійний клік Ð´Ð»Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½ÑŒ&lt;br&gt;ПеретÑгніть екран у кошик Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">ÐšÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ Ñервера</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">Екрани Ñ– зв'Ñзки</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">ПеретÑгніть екран у кошик Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">Ðалаштувати Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— Ñервера Barrier .</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">ПеретÑгніть цю кнопку на Ñітку, щоб додати новий екран.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">ПеретÑгуйте нові екрани на Ñітку або переÑувайте Ñ–Ñнуючі міÑцÑми.
+ПеретÑгніть екран в кошик, щоб видалити його.
+Подвійний клік на екрані Ð´Ð»Ñ Ð¹Ð¾Ð³Ð¾ налаштуваннÑ.</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">ГарÑчі клавіші</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">ГарÑчі клавіші</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">Ðовий</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">РедагуваннÑ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Видалити</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">Дії</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">Ðовий</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">Редагувати</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">Видалити</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">Додаткові Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñервера</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">Перехід</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">Перехід піÑÐ»Ñ Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">мÑек</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">Перехід піÑÐ»Ñ Ð¿Ð¾Ð´Ð²Ñ–Ð¹Ð½Ð¾Ð³Ð¾ натиÑÐºÐ°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ‚Ñгом</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">Опції</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">ПеревірÑти клієнтів кожні</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">ВикориÑтовувати рідні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÑƒÑ€Ñору</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">Синхронізувати заÑтавки</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">Ðе залишати вікно Ñервера зверху (у Windows)</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">Мертві кути</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">Верхній лівий</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">Верхній правий</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">Ðижній лівий</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">Ðижній правий</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">Розмір кута:</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">Зберегти log-файл в...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">Підвищіть рівень доÑтупу Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">ДійÑно бажаєте підвищить рівень доÑтупу Barrier?
+Ð¦Ñ Ð´Ñ–Ñ Ð´Ð¾Ð·Ð²Ð¾Ð»Ð¸Ñ‚ÑŒ Barrier бути у взаємодії з процеÑами, Ñкі мають підвищеній рівень доÑтупу, та діалогу UAC (контроль облікових запиÑів кориÑтувачів). Ðле це може викликати проблеми з процеÑами, Ñкі мають звичайний рівень доÑтупу. Підвищуйте рівень доÑтупу Barrier тільки в тому випадку, коли це дійÑно потрібно.</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">ÐалаштуваннÑ</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">Ім'Ñ ÐµÐºÑ€Ð°Ð½Ñƒ:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">Порт:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">ІнтерфейÑ:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">ЗапиÑ</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">Рівень запиÑу:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">ЗапиÑувати у фал:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">ПереглÑд...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">Помилка</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Мова:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">&amp;Додатково</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">ПопередженнÑ</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">Примітка</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">ІнформаціÑ</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">ÐалагодженнÑ</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">ÐалагодженнÑ1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">ÐалагодженнÑ2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">Будь лаÑка, оберіть опцію.</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Будь лаÑка вкажіть адреÑу вашої електронної пошти та пароль.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">Ð’ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">Вітаемо</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">ДÑкуємо за вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier дозволить Вам легко викориÑтовувати мишку та клавіатуру між декількома комп'ютерами за вашим Ñтолом, Ñ– це зовÑім безкоштовний та відкритий продукт. Тільки підведіть курÑор мишки до краю одного з комп'ютерів Ñ– він автоматично переміÑтитьÑÑ Ð½Ð° інший. Також можна викориÑтовувати один буфер обміну між декількома комп'ютерами. Ð’Ñе що необхідно, це мережеве з'єднаннÑ. Barrier ÑвлÑєтьÑÑ Ð¼Ñ–Ð¶Ð¿Ð»Ð°Ñ‚Ñ„Ð¾Ñ€Ð¼Ð¾Ð²Ð¸Ð¼ продуктом (працює з Windows, Mac OS X та Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">Сервер чи клієнт?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Ðевідомо</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Вхід ÑкаÑовано, неправильна адреÑа або пароль.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">Вхід ÑкаÑовано, виникла помилка.
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">Вхід ÑкаÑовано, виникла помилка.
+Відповідь Ñервера:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_ur.qm b/src/gui/res/lang/gui_ur.qm
new file mode 100644
index 0000000..c546613
--- /dev/null
+++ b/src/gui/res/lang/gui_ur.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_ur.ts b/src/gui/res/lang/gui_ur.ts
new file mode 100644
index 0000000..2dbd1ea
--- /dev/null
+++ b/src/gui/res/lang/gui_ur.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="ur" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">معلومات</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier آپ Ú©Ùˆ آسانی سے اپنا ما٘وس اور کیبورڈ متعدد کمپیوٹرز Ú©Û’ ساتھ استعمال کرنے Ú©ÛŒ سÛولت میسر کرتا ÛÛ’Û”Barrier Ù…Ùت اور اوپن سورس ÛÛ’Û” صر٠ایک کمپیوٹر اسکرین Ú©Û’ کنارے سے دوسرے پر اپنے ماؤس Ù„Û’ جائیں اور بس۔ آپ تمام کمپیوٹرز Ú©Û’ کلپ بورڈ ایک دوسرے Ú©Û’ ساتھ تبدیل بھی کر سکتے Ûیں۔ ÛŒÛ Ø³Ø¨ کرنے Ú©Û’ لیے صر٠ایک نیٹ ورک کنکشن Ú©ÛŒ ضرورت ÛÛ’Û” Barrier متعدد آپریٹنگ سسٹمز (ونڈوز، میک OS X اور لینکس) Ú©Û’ ساتھ استعمال کیا جا سکتا ÛÛ’ </translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_vi.qm b/src/gui/res/lang/gui_vi.qm
new file mode 100644
index 0000000..12ce570
--- /dev/null
+++ b/src/gui/res/lang/gui_vi.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_vi.ts b/src/gui/res/lang/gui_vi.ts
new file mode 100644
index 0000000..e4031c5
--- /dev/null
+++ b/src/gui/res/lang/gui_vi.ts
@@ -0,0 +1,1405 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="vi" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">Thông tin vỠBarrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">Không biết</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">Phiên bản:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">Ok</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">trái</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">phải</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">lên</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">xuống</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">bật</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">tắt</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">Lệnh này được thực hiện khi </translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Bắt đầu</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;Phiên bản Barrier của bạn đã cÅ©. Phiên bản má»›i &lt;b&gt;%1&lt;/b&gt; đã có để &lt;a href=&quot;%2&quot;&gt;tải vá»&lt;/a&gt;.&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">Chương trình không thể khởi động.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">Không tìm thấy máy chủ Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier đã ngừng vì lỗi </translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">Dừng</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier đang khởi động.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier đang chạy.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier không chạy.</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">Không biết</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">Bắt đầu</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">Sẵn sàng</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">Thoát</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">Chạy</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">Dừng</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">Hiển thị tình trạng</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">Cài đặt</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">Chỉnh sửa cài đặt</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">Thêm</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Xóa</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Shift: </translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl </translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">Không</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">Xóa</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">Cài đặt</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">Ngôn ngữ:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">Thông tin</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">Hãy nhập tài khoản email và mật khẩu.</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Sức mạnh tổng hợp cho phép bạn dá»… dàng chia sẻ chuá»™t và bàn phím giữa nhiá»u máy tính trên bàn của bạn, và nó là miá»…n phí và mã nguồn mở. Chỉ cần di chuyển chuá»™t của bạn khá»i các cạnh của màn hình má»™t máy tính khác. Bạn thậm chí có thể chia sẻ tất cả các clipboards của bạn. Tất cả bạn cần là má»™t kết nối mạng. Barrier là ná»n tảng chéo (hoạt Ä‘á»™ng trên Windows, Mac OS X và Linux).</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">Không biết</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">Äăng nhập thất bại, tài khoản email hoặc mật khẩu không hợp lệ.</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_zh-CN.qm b/src/gui/res/lang/gui_zh-CN.qm
new file mode 100644
index 0000000..ab5415e
--- /dev/null
+++ b/src/gui/res/lang/gui_zh-CN.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_zh-CN.ts b/src/gui/res/lang/gui_zh-CN.ts
new file mode 100644
index 0000000..b5cdf6c
--- /dev/null
+++ b/src/gui/res/lang/gui_zh-CN.ts
@@ -0,0 +1,1411 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="zh-CN" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">关于Barrier</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">未知</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">版本:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">确定</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">行为é…ç½®</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">选择è¦æ‰§è¡Œçš„行为</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">按下热键</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">æ¾å¼€çƒ­é”®</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">按下一个键然åŽæ¾å¼€</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">仅仅在这些å±å¹•ä¸Š</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">切æ¢åˆ°å±å¹•</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">æ–¹å‘切æ¢</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">å·¦</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">å³ä¾§</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">上方</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">下方</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">é”定指针于å±å¹•</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">切æ¢</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">å¯ç”¨</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">ç¦ç”¨</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">当……时å¯ç”¨æ­¤è¡Œä¸º</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">热键被按下</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">热键被æ¾å¼€</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">热键</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">输入热键的说明:</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">开始</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">文件</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">编辑</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">窗å£</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">帮助</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;您正在使用的Barrier版本有些过时了,有新版 &lt;b&gt;%1&lt;/b&gt; å¯ä»¥ &lt;a href=&quot;%2&quot;&gt;下载&lt;/a&gt;。&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">程åºæ— æ³•å¯åŠ¨</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">å¯æ‰§è¡Œç¨‹åº&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;没有æˆåŠŸè¿è¡Œï¼Œè™½ç„¶ç¨‹åºæœ¬èº«å­˜åœ¨ã€‚请检查你是å¦æœ‰è¿è¡Œæ­¤ç¨‹åºçš„æƒé™ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">未找到Barrier客户端</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Barrier客户端的å¯æ‰§è¡Œç¨‹åºä¸å­˜åœ¨ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">主机å为空</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">请为Barrier客户端设置一个用于连接的主机å</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">ä¸èƒ½å†™å…¥é…置文件</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">å¯åŠ¨Barrier所需的临时é…置文件ä¸å¯å†™ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">é…置文件åéžæ³•</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">你没有为BarrieræœåŠ¡ç«¯è®¾ç½®ä¸€ä¸ªå¯ç”¨çš„é…置文件。需è¦çŽ°åœ¨æµè§ˆé…置文件å—?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">未找到BarrieræœåŠ¡ç«¯</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">BarrieræœåŠ¡ç«¯å¯æ‰§è¡Œç¨‹åºä¸å­˜åœ¨ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrier因错终止è¿è¡Œ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrieræ„外终止è¿è¡Œï¼Œé€€å‡ºä»£ç  %1。&lt;br&gt;&lt;br&gt;请查看输出日志了解详情。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">åœæ­¢</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier正在å¯åŠ¨</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier正在è¿è¡Œ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrier没有è¿è¡Œ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">未知</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">æµè§ˆBarrieré…置文件</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">ä¿å­˜é…置到文件</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">ä¿å­˜å¤±è´¥</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">ä¸èƒ½ä¿å­˜é…置到文件</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">å±å¹•å:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">æœåŠ¡ç«¯IP</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">开始</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">使用已有的é…置:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">é…置文件:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">æµè§ˆâ€¦</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">交互é…置:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">设置æœåŠ¡ç«¯â€¦</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">准备完毕</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">日志</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">应用</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP地å€</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">关于Barrier…</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">退出</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">退出</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">è¿è¡Œ</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">åœæ­¢</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">åœæ­¢</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">显示状æ€</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">éšè—</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">éšè—</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">显示</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">显示</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">ä¿å­˜é…置到…</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">ä¿å­˜é€šè¿‡äº¤äº’é…置生æˆçš„é…置到文件。</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">设置</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">编辑设置</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">è¿è¡Œå‘导</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">未命å</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">设置Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrieré…置文件(*.sgc);;所有文件 (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrieré…置文件(*.conf);;所有文件 (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">系统托盘ä¸å¯ç”¨ï¼Œç¨‹åºé€€å‡ºã€‚</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">å±å¹•å为空</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">å±å¹•åä¸èƒ½ä¸ºç©ºã€‚请填入一个å字或者关闭对è¯æ¡†ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">å±å¹•å对应别å</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">å±å¹•åä¸èƒ½ä¸Žåˆ«å相åŒï¼Œè¯·å–消或者更改别å。</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">å±å¹•è®¾ç½®</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">å±å¹•å:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">别å</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">添加</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">删除</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">修改按键</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">Shift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">超级</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">æ— </translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">Ctrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">Alt:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">Meta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">Super:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">死角</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">左上角</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">å³ä¸Šè§’</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">左下角</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">å³ä¸‹è§’</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">死角大å°</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">修改</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">ä¿®å¤caps locké”®</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">ä¿®å¤num locké”®</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">ä¿®å¤scroll locké”®</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">ä¿®å¤Xineramaçš„XTest</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;å±å¹•è®¾ç½®: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;åŒå‡»ä»¥ä¿®æ”¹è®¾ç½®&lt;br&gt;å°†å±å¹•æ‹–到废纸篓æ¥ç§»é™¤</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">æœåŠ¡ç«¯é…ç½®</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">å±å¹•å’Œè”接</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">从格å­ä¸­æ‹–动å±å¹•åˆ°åžƒåœ¾æ¡¶è¿›è¡Œåˆ é™¤ã€‚</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">设置BarrieræœåŠ¡ç«¯é…置的å±å¹•å¸ƒå±€ã€‚</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">拖动此按钮到格å­ä¸­è¿›è¡Œæ·»åŠ å±å¹•ã€‚</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">拖动å±å¹•ï¼ˆå›¾æ ‡ï¼‰åˆ°ç½‘格中或者移动已ç»åœ¨ç½‘格中的å±å¹•çš„ä½ç½®ã€‚
+拖动å±å¹•åˆ°åžƒåœ¾æ¡¶è¿›è¡Œåˆ é™¤ã€‚
+åŒå‡»å±å¹•ç¼–辑其设置。</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">热键</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">热键</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">新建</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">编辑</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">删除</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">行为</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">新建</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">编辑</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">删除</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">æœåŠ¡ç«¯é«˜çº§è®¾ç½®</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">切æ¢</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">等待åŽåˆ‡æ¢</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">毫秒</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">åŒå‡»tap切æ¢</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">选项</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">客户端检查周期</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">使用相关的鼠标动作</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">åŒæ­¥å±å¹•ä¿æŠ¤</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">ä¸è¦ç§»åŠ¨å‰å°çª—å£åœ¨WindowsæœåŠ¡å™¨ä¸Š</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">死角</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">左上角</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">å³ä¸Šè§’</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">左下角</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">å³ä¸‹è§’</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">死角大å°</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">ä¿å­˜æ—¥å¿—文件…</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">评价Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">您确定è¦elevate Barrierå—?
+这会å…许Barrierå’Œelevated进程交互和UAC对è¯æ¡†ï¼Œä½†æ˜¯å¯èƒ½å¼•èµ·ä¸€åˆ‡å’Œéželevated进程交互的问题。åªæœ‰åœ¨ä½ éœ€è¦çš„时候æ‰Elevate Barrier。</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">设置</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">å±å¹•å称:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">端å£ï¼š</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">ç•Œé¢</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">日志记录</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">日志等级</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">记录到文件</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">æµè§ˆâ€¦</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">错误</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">语言:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">其他</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">警告</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">注æ„</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">ä¿¡æ¯</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">调试</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">调试1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">调试2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">设置Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">请选择一个选项</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">请输入您的邮箱地å€å’Œå¯†ç ã€‚</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">设置Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">欢迎</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">感谢您安装Barrierï¼</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrierå…许你轻æ¾åœ°åœ¨ä½ åŠžå…¬æ¡Œä¸Šå¤šå°è®¡ç®—机之间共享你的鼠标和键盘,它å…费并且开放æºä»£ç ã€‚ä½ åªè¦å°†é¼ æ ‡ï¼ˆæŒ‡é’ˆï¼‰ä»Žä¸€å°è®¡ç®—机的å±å¹•è¾¹ç¼˜ç§»å‡ºåˆ°å¦ä¸€ä¸ªå±å¹•å°±è¡Œäº†ã€‚甚至å¯ä»¥å…±äº«ä½ çš„剪贴æ¿ã€‚你所需è¦çš„仅仅是一个网络连接。Barrier是跨平å°çš„(å¯ä»¥è¿è¡ŒäºŽWindows,Mac OS Xå’ŒLinux)。</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">(此计算机作为)æœåŠ¡ç«¯è¿˜æ˜¯å®¢æˆ·ç«¯ï¼Ÿ</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">未知</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">登录失败,邮箱地å€æˆ–密ç é”™è¯¯ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">登录失败,出错了。
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">登录失败,出错了。
+æœåŠ¡å™¨å›žåº”:
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/lang/gui_zh-TW.qm b/src/gui/res/lang/gui_zh-TW.qm
new file mode 100644
index 0000000..92e5e3a
--- /dev/null
+++ b/src/gui/res/lang/gui_zh-TW.qm
Binary files differ
diff --git a/src/gui/res/lang/gui_zh-TW.ts b/src/gui/res/lang/gui_zh-TW.ts
new file mode 100644
index 0000000..fc55f56
--- /dev/null
+++ b/src/gui/res/lang/gui_zh-TW.ts
@@ -0,0 +1,1414 @@
+<?xml version="1.0" encoding="utf-8"?><!DOCTYPE TS><TS language="zh-TW" sourcelanguage="en" version="2.0">
+<context>
+ <name>AboutDialogBase</name>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="38"/>
+ <source>About Barrier</source>
+ <translation type="finished">關於Barrier...</translation>
+ </message>
+ <message utf8="true">
+ <location filename="res/AboutDialogBase.ui" line="53"/>
+ <source>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Visit our website for help and info (symless.com).
+&lt;/p&gt;</oldsource>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="140"/>
+ <source>Unknown</source>
+ <translation type="finished">未知</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="124"/>
+ <source>Version:</source>
+ <translation type="finished">版本:</translation>
+ </message>
+ <message>
+ <location filename="res/AboutDialogBase.ui" line="163"/>
+ <source>&amp;Ok</source>
+ <translation type="finished">確定</translation>
+ </message>
+</context>
+<context>
+ <name>ActionDialogBase</name>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="14"/>
+ <source>Configure Action</source>
+ <translation type="finished">設定動作</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="20"/>
+ <source>Choose the action to perform</source>
+ <translation type="finished">é¸æ“‡è¦åŸ·è¡Œçš„動作</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="26"/>
+ <source>Press a hotkey</source>
+ <translation type="finished">按下快æ·éµ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="36"/>
+ <source>Release a hotkey</source>
+ <translation type="finished">放開快æ·éµ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="43"/>
+ <source>Press and release a hotkey</source>
+ <translation type="finished">按下後放開快æ·éµ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="69"/>
+ <source>only on these screens</source>
+ <translation type="finished">åªåœ¨ä¸‹åˆ—螢幕顯示</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="119"/>
+ <source>Switch to screen</source>
+ <translation type="finished">切æ›è‡³</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="150"/>
+ <source>Switch in direction</source>
+ <translation type="finished">切æ›æ–¹å‘</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="174"/>
+ <source>left</source>
+ <translation type="finished">å·¦</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="179"/>
+ <source>right</source>
+ <translation type="finished">å³</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="184"/>
+ <source>up</source>
+ <translation type="finished">上</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="189"/>
+ <source>down</source>
+ <translation type="finished">下</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="201"/>
+ <source>Lock cursor to screen</source>
+ <translation type="finished">鎖定至</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="225"/>
+ <source>toggle</source>
+ <translation type="finished">轉æ›</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="230"/>
+ <source>on</source>
+ <translation type="finished">é–‹å•Ÿ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="235"/>
+ <source>off</source>
+ <translation type="finished">關閉</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="248"/>
+ <source>This action is performed when</source>
+ <translation type="finished">執行此動作,當</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="254"/>
+ <source>the hotkey is pressed</source>
+ <translation type="finished">已按下快æ·éµ</translation>
+ </message>
+ <message>
+ <location filename="res/ActionDialogBase.ui" line="264"/>
+ <source>the hotkey is released</source>
+ <translation type="finished">已放開快æ·éµ</translation>
+ </message>
+</context>
+<context>
+ <name>AddClientDialog</name>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="20"/>
+ <source>Dialog</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="35"/>
+ <source>TextLabel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/AddClientDialogBase.ui" line="83"/>
+ <source>Ignore auto connect clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>HotkeyDialogBase</name>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="14"/>
+ <source>Hotkey</source>
+ <translation type="finished">å¿«æ·éµ</translation>
+ </message>
+ <message>
+ <location filename="res/HotkeyDialogBase.ui" line="20"/>
+ <source>Enter the specification for the hotkey:</source>
+ <translation type="finished">輸入快æ·éµè¨­å®šï¼š</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="790"/>
+ <source>&amp;Start</source>
+ <translation type="finished">å•Ÿå‹•</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="237"/>
+ <source>&amp;File</source>
+ <translation type="finished">檔案</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="238"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">編輯</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="239"/>
+ <source>&amp;Window</source>
+ <translation type="finished">視窗</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="240"/>
+ <source>&amp;Help</source>
+ <translation type="finished">幫助</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="364"/>
+ <source>&lt;p&gt;Your version of Barrier is out of date. Version &lt;b&gt;%1&lt;/b&gt; is now available to &lt;a href=&quot;%2&quot;&gt;download&lt;/a&gt;.&lt;/p&gt;</source>
+ <oldsource>&lt;p&gt;Version %1 is now available, &lt;a href=&quot;%2&quot;&gt;visit website&lt;/a&gt;.&lt;/p&gt;</oldsource>
+ <translation type="finished">&lt;p&gt;ä½ çš„Barrier版本已經太舊, 版本 &lt;b&gt;%1&lt;/b&gt;已經å¯ä»¥è‡³ &lt;a href=&quot;%2&quot;&gt;下載&lt;/a&gt;。&lt;/p&gt;</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>Program can not be started</source>
+ <translation type="finished">未能啟動程åº</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="577"/>
+ <source>The executable&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.</source>
+ <translation type="finished">未能æˆåŠŸå•Ÿå‹•åŸ·è¡Œæª”&lt;br&gt;&lt;br&gt;%1&lt;br&gt;&lt;br&gt;,儘管該檔案存在,請檢查執行程åºæ¬Šé™è¨­å®šã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="596"/>
+ <source>Barrier client not found</source>
+ <translation type="finished">找ä¸åˆ°Barrier客戶端</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="597"/>
+ <source>The executable for the barrier client does not exist.</source>
+ <translation type="finished">Barrier客戶端執行檔ä¸å­˜åœ¨ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="625"/>
+ <source>Hostname is empty</source>
+ <translation type="finished">電腦å稱ä¸èƒ½ç©ºç™½</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="626"/>
+ <source>Please fill in a hostname for the barrier client to connect to.</source>
+ <translation type="finished">請輸入barrier客戶端è¦é€£æŽ¥çš„電腦å稱。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>Cannot write configuration file</source>
+ <translation type="finished">無法寫入設定檔</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="646"/>
+ <source>The temporary configuration file required to start barrier can not be written.</source>
+ <translation type="finished">無法寫入用以啟動barrier的暫存設定檔</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="659"/>
+ <source>Configuration filename invalid</source>
+ <translation type="finished">設定檔檔案å稱無效</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="660"/>
+ <source>You have not filled in a valid configuration file for the barrier server. Do you want to browse for the configuration file now?</source>
+ <translation type="finished">你沒有輸入有效的barrier伺æœå™¨è¨­å®šæª”,你需è¦ç¾åœ¨ç€è¦½è¨­å®šæª”嗎?</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="688"/>
+ <source>Barrier server not found</source>
+ <translation type="finished">找ä¸åˆ°Barrier伺æœå™¨</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="689"/>
+ <source>The executable for the barrier server does not exist.</source>
+ <translation type="finished">Barrier伺æœå™¨åŸ·è¡Œæª”ä¸å­˜åœ¨ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated with an error</source>
+ <translation type="finished">Barrieré‡åˆ°éŒ¯èª¤ï¼Œåœæ­¢é‹ä½œ</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="764"/>
+ <source>Barrier terminated unexpectedly with an exit code of %1.&lt;br&gt;&lt;br&gt;Please see the log output for details.</source>
+ <translation type="finished">Barrieré‡åˆ°éŒ¯èª¤ï¼Œåœæ­¢é‹ä½œï¼ŒéŒ¯èª¤ç¢¼ %1&lt;br&gt;&lt;br&gt;詳細情æ³å¯åƒè€ƒè¨˜éŒ„日誌。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="783"/>
+ <source>&amp;Stop</source>
+ <translation type="finished">åœæ­¢</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1038"/>
+ <source>Please add the server (%1) to the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1044"/>
+ <source>Please drag the new client screen (%1) to the desired position on the grid.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1147"/>
+ <source>Failed to detect system architecture.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1165"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1189"/>
+ <source>Failed to download Bonjour installer to location: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1226"/>
+ <source>Do you want to enable auto config and install Bonjour?
+
+This feature helps you establish the connection.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1270"/>
+ <source>Auto config feature requires Bonjour.
+
+Do you want to install Bonjour?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="815"/>
+ <source>Barrier is starting.</source>
+ <translation type="finished">Barrier正在啟動中。</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="809"/>
+ <source>Barrier is running.</source>
+ <translation type="finished">Barrier正在é‹ä½œä¸­ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="819"/>
+ <source>Barrier is not running.</source>
+ <translation type="finished">Barrierä¸åœ¨é‹ä½œä¸­ã€‚</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="870"/>
+ <source>Unknown</source>
+ <translation type="finished">未知</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1146"/>
+ <location filename="src/MainWindow.cpp" line="1225"/>
+ <location filename="src/MainWindow.cpp" line="1269"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="987"/>
+ <source>Browse for a barriers config file</source>
+ <translation type="finished">ç€è¦½ä»¥é¸æ“‡è¨­å®šæª”</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="408"/>
+ <source>Barrier is now connected, You can close the config window. Barrier will remain connected in the background.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="434"/>
+ <source>Security question</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="435"/>
+ <source>Do you trust this fingerprint?
+
+%1
+
+This is a server fingerprint. You should compare this fingerprint to the one on your server's screen. If the two don't match exactly, then it's probably not the server you're expecting (it could be a malicious user).
+
+To automatically trust this fingerprint for future connections, click Yes. To reject this fingerprint and disconnect from the server, click No.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1000"/>
+ <source>Save configuration as...</source>
+ <translation type="finished">儲存設定至...</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Save failed</source>
+ <translation type="finished">儲存失敗</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="1004"/>
+ <source>Could not save configuration to file.</source>
+ <translation type="finished">無法儲存設定至檔案。</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindowBase</name>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="26"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="90"/>
+ <source>Ser&amp;ver (share this computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="243"/>
+ <source>Screen name:</source>
+ <translation type="finished">顯示å稱:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="257"/>
+ <source>&amp;Server IP:</source>
+ <translation type="finished">伺æœå™¨(&amp;S) IP:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="380"/>
+ <location filename="res/MainWindowBase.ui" line="409"/>
+ <source>&amp;Start</source>
+ <translation type="finished">å•Ÿå‹•</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="181"/>
+ <source>Use existing configuration:</source>
+ <translation type="finished">使用舊有設定檔:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="190"/>
+ <source>&amp;Configuration file:</source>
+ <translation type="finished">設定檔:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="210"/>
+ <source>&amp;Browse...</source>
+ <translation type="finished">ç€è¦½...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="147"/>
+ <source>Configure interactively:</source>
+ <translation type="finished">互動地設定:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="159"/>
+ <source>&amp;Configure Server...</source>
+ <translation type="finished">設定伺æœå™¨...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="350"/>
+ <source>Ready</source>
+ <translation type="finished">準備</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="296"/>
+ <source>Log</source>
+ <translation type="finished">記錄</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="373"/>
+ <source>&amp;Apply</source>
+ <translation type="finished">應用</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="107"/>
+ <source>IP addresses:</source>
+ <translation type="finished">IP地å€:</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="131"/>
+ <source>Fingerprint:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="228"/>
+ <source>&amp;Client (use another computer's mouse and keyboard):</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="270"/>
+ <source>Auto config</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="390"/>
+ <source>&amp;About Barrier...</source>
+ <translation type="finished">關於Barrier...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="398"/>
+ <source>&amp;Quit</source>
+ <translation type="finished">離開</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="401"/>
+ <source>Quit</source>
+ <translation type="finished">離開</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="412"/>
+ <source>Run</source>
+ <translation type="finished">執行</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="423"/>
+ <source>S&amp;top</source>
+ <translation type="finished">åœæ­¢</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="426"/>
+ <source>Stop</source>
+ <translation type="finished">åœæ­¢</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="434"/>
+ <source>S&amp;how Status</source>
+ <translation type="finished">顯示狀態</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="442"/>
+ <source>&amp;Hide</source>
+ <translation type="finished">éš±è—</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="445"/>
+ <source>Hide</source>
+ <translation type="finished">éš±è—</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="453"/>
+ <source>&amp;Show</source>
+ <translation type="finished">顯示(&amp;S)</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="456"/>
+ <source>Show</source>
+ <translation type="finished">顯示</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="464"/>
+ <source>Save configuration &amp;as...</source>
+ <translation type="finished">儲存設定至...</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="467"/>
+ <source>Save the interactively generated server configuration to a file.</source>
+ <translation type="finished">儲存動態生æˆçš„設定至檔案。</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="475"/>
+ <source>Settings</source>
+ <translation type="finished">設定</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="478"/>
+ <source>Edit settings</source>
+ <translation type="finished">編輯</translation>
+ </message>
+ <message>
+ <location filename="res/MainWindowBase.ui" line="486"/>
+ <source>Run Wizard</source>
+ <translation type="finished">執行設定精éˆ</translation>
+ </message>
+</context>
+<context>
+ <name>NewScreenWidget</name>
+ <message>
+ <location filename="src/NewScreenWidget.cpp" line="32"/>
+ <source>Unnamed</source>
+ <translation type="finished">未命å</translation>
+ </message>
+</context>
+<context>
+ <name>PluginManager</name>
+ <message>
+ <location filename="src/PluginManager.cpp" line="58"/>
+ <source>Failed to get plugin directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="63"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="136"/>
+ <source>Failed to download plugin '%1' to: %2
+%3</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="167"/>
+ <source>Could not get Windows architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginManager.cpp" line="191"/>
+ <source>Could not get Linux architecture type.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PluginWizardPage</name>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="14"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">設定Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/PluginWizardPageBase.ui" line="101"/>
+ <source>Please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="72"/>
+ <source>Error: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="80"/>
+ <location filename="src/PluginWizardPage.cpp" line="201"/>
+ <source>Setup complete.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="93"/>
+ <source>Downloading '%1' plugin (%2/%3)...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="104"/>
+ <source>Plugins installed successfully.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="120"/>
+ <source>Generating SSL certificate...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="170"/>
+ <source>Downloading plugin: %1 (1/%2)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/PluginWizardPage.cpp" line="239"/>
+ <source>Getting plugin list...</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="src/MainWindow.cpp" line="60"/>
+ <source>Barrier Configurations (*.sgc);;All files (*.*)</source>
+ <translation type="finished">Barrier 設定檔 (*.sgc);;所有檔案 (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/MainWindow.cpp" line="67"/>
+ <source>Barrier Configurations (*.conf);;All files (*.*)</source>
+ <translation type="finished">Barrier 設定檔 (*.conf);;所有檔案 (*.*)</translation>
+ </message>
+ <message>
+ <location filename="src/main.cpp" line="119"/>
+ <source>System tray is unavailable, quitting.</source>
+ <translation type="finished">未能å–得系統工具欄,正在離開。</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialog</name>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="67"/>
+ <source>Screen name is empty</source>
+ <translation type="finished">顯示å稱ä¸èƒ½ç©ºç™½</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="68"/>
+ <source>The screen name cannot be empty. Please either fill in a name or cancel the dialog.</source>
+ <translation type="finished">Screen name必填,請填入screen name或å–消。</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="83"/>
+ <source>Screen name matches alias</source>
+ <translation type="finished">顯示å稱相åŒæ–¼åˆ¥å</translation>
+ </message>
+ <message>
+ <location filename="src/ScreenSettingsDialog.cpp" line="84"/>
+ <source>The screen name cannot be the same as an alias. Please either remove the alias or change the screen name.</source>
+ <translation type="finished">Screen nameä¸èƒ½èˆ‡åˆ¥å相åŒï¼Œè«‹åˆªé™¤åˆ¥å或修改screen name。</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSettingsDialogBase</name>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="14"/>
+ <source>Screen Settings</source>
+ <translation type="finished">顯示設定</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="22"/>
+ <source>Screen &amp;name:</source>
+ <translation type="finished">顯示å稱:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="42"/>
+ <source>A&amp;liases</source>
+ <translation type="finished">別å</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="57"/>
+ <source>&amp;Add</source>
+ <translation type="finished">添加</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="74"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">移除</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="97"/>
+ <source>&amp;Modifier keys</source>
+ <translation type="finished">功能éµ</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="106"/>
+ <source>&amp;Shift:</source>
+ <translation type="finished">&amp;undefinedShift:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="117"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="164"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="211"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="258"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="305"/>
+ <source>Shift</source>
+ <translation type="finished">Shift</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="122"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="169"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="216"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="263"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="310"/>
+ <source>Ctrl</source>
+ <translation type="finished">Ctrl</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="127"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="174"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="221"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="268"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="315"/>
+ <source>Alt</source>
+ <translation type="finished">Alt</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="132"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="179"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="226"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="273"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="320"/>
+ <source>Meta</source>
+ <translation type="finished">Meta</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="137"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="184"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="231"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="278"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="325"/>
+ <source>Super</source>
+ <translation type="finished">Super</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="142"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="189"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="236"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="283"/>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="330"/>
+ <source>None</source>
+ <translation type="finished">None</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="150"/>
+ <source>&amp;Ctrl:</source>
+ <translation type="finished">&amp;undefinedCtrl:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="197"/>
+ <source>Al&amp;t:</source>
+ <translation type="finished">&amp;undefinedl&amp;t:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="244"/>
+ <source>M&amp;eta:</source>
+ <translation type="finished">&amp;undefined&amp;eta:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="291"/>
+ <source>S&amp;uper:</source>
+ <translation type="finished">&amp;undefined&amp;uper:</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="358"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">死角</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="367"/>
+ <source>Top-left</source>
+ <translation type="finished">左上</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="374"/>
+ <source>Top-right</source>
+ <translation type="finished">å³ä¸Š</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="381"/>
+ <source>Bottom-left</source>
+ <translation type="finished">左下</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="388"/>
+ <source>Bottom-right</source>
+ <translation type="finished">å³ä¸‹</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="397"/>
+ <source>Corner Si&amp;ze:</source>
+ <translation type="finished">角è½å¤§å°ï¼š</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="428"/>
+ <source>&amp;Fixes</source>
+ <translation type="finished">修正</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="437"/>
+ <source>Fix CAPS LOCK key</source>
+ <translation type="finished">修正CAPS LOCKéµ</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="444"/>
+ <source>Fix NUM LOCK key</source>
+ <translation type="finished">修正NUM LOCKéµ</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="451"/>
+ <source>Fix SCROLL LOCK key</source>
+ <translation type="finished">修正SCROLL LOCKéµ</translation>
+ </message>
+ <message>
+ <location filename="res/ScreenSettingsDialogBase.ui" line="458"/>
+ <source>Fix XTest for Xinerama</source>
+ <translation type="finished">修正Xinerama的XTest</translation>
+ </message>
+</context>
+<context>
+ <name>ScreenSetupModel</name>
+ <message>
+ <location filename="src/ScreenSetupModel.cpp" line="51"/>
+ <source>&lt;center&gt;Screen: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;Double click to edit settings&lt;br&gt;Drag screen to the trashcan to remove it</source>
+ <translation type="finished">&lt;center&gt;螢幕: &lt;b&gt;%1&lt;/b&gt;&lt;/center&gt;&lt;br&gt;雙擊以編輯設定&lt;br&gt;拖動螢幕至垃圾筒以移除</translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialog</name>
+ <message>
+ <location filename="src/ServerConfigDialog.cpp" line="75"/>
+ <source>Configure server</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ServerConfigDialogBase</name>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="14"/>
+ <source>Server Configuration</source>
+ <translation type="finished">伺æœå™¨è¨­å®š</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="24"/>
+ <source>Screens and links</source>
+ <translation type="finished">螢幕和連çµ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="35"/>
+ <source>Drag a screen from the grid to the trashcan to remove it.</source>
+ <translation type="finished">從網格上拖動螢幕至垃圾筒以移除</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="60"/>
+ <source>Configure the layout of your barrier server configuration.</source>
+ <translation type="finished">設定barrier伺æœå™¨è¨­å®šä½ˆå±€</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="73"/>
+ <source>Drag this button to the grid to add a new screen.</source>
+ <translation type="finished">拖動此按鈕至網格以添加新螢幕</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="128"/>
+ <source>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</source>
+ <translation type="finished">拖動新螢幕至網格或在網格上拖動螢幕以改變佈局ä½ç½®ã€‚
+拖動螢幕至垃圾筒以移除。
+雙擊螢幕以編輯設定。</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="157"/>
+ <source>Hotkeys</source>
+ <translation type="finished">å¿«æ·éµ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="163"/>
+ <source>&amp;Hotkeys</source>
+ <translation type="finished">å¿«æ·éµ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="175"/>
+ <source>&amp;New</source>
+ <translation type="finished">新建</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="185"/>
+ <source>&amp;Edit</source>
+ <translation type="finished">編輯</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="195"/>
+ <source>&amp;Remove</source>
+ <translation type="finished">移除</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="218"/>
+ <source>A&amp;ctions</source>
+ <translation type="finished">動作</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="230"/>
+ <source>Ne&amp;w</source>
+ <translation type="finished">新建</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="240"/>
+ <source>E&amp;dit</source>
+ <translation type="finished">編輯</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="250"/>
+ <source>Re&amp;move</source>
+ <translation type="finished">移除</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="274"/>
+ <source>Advanced server settings</source>
+ <translation type="finished">伺æœå™¨é€²éšŽè¨­å®š</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="280"/>
+ <source>&amp;Switch</source>
+ <translation type="finished">切æ›</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="291"/>
+ <source>Switch &amp;after waiting</source>
+ <translation type="finished">切æ›å‰ç¨å¾Œ</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="330"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="383"/>
+ <location filename="res/ServerConfigDialogBase.ui" line="458"/>
+ <source>ms</source>
+ <translation type="finished">微秒</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="344"/>
+ <source>Switch on double &amp;tap within</source>
+ <translation type="finished">於多少時間內雙擊滑鼠進行切æ›</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="408"/>
+ <source>&amp;Options</source>
+ <translation type="finished">é¸é …</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="419"/>
+ <source>&amp;Check clients every</source>
+ <translation type="finished">æ¯éš”多少時間檢查å„客戶端</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="470"/>
+ <source>Use &amp;relative mouse moves</source>
+ <translation type="finished">使用滑鼠相å°ç§»å‹•æ–¹å¼</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="480"/>
+ <source>S&amp;ynchronize screen savers</source>
+ <translation type="finished">åŒæ­¥èž¢å¹•ä¿è­·ç¨‹å¼</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="490"/>
+ <source>Don't take &amp;foreground window on Windows servers</source>
+ <translation type="finished">在Windows伺æœå™¨ä¸Šä¸­ä¸ç²å–å‰æ™¯è¦–窗</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="510"/>
+ <source>Ignore auto config clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="520"/>
+ <source>&amp;Dead corners</source>
+ <translation type="finished">死角</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="529"/>
+ <source>To&amp;p-left</source>
+ <translation type="finished">左上方</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="536"/>
+ <source>Top-rig&amp;ht</source>
+ <translation type="finished">å³ä¸Šæ–¹</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="543"/>
+ <source>&amp;Bottom-left</source>
+ <translation type="finished">左下方</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="550"/>
+ <source>Bottom-ri&amp;ght</source>
+ <translation type="finished">å³ä¸‹æ–¹</translation>
+ </message>
+ <message>
+ <location filename="res/ServerConfigDialogBase.ui" line="572"/>
+ <source>Cor&amp;ner Size:</source>
+ <translation type="finished">角è½å¤§å°ï¼š</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="131"/>
+ <source>Save log file to...</source>
+ <translation type="finished">儲存記錄至檔案...</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="151"/>
+ <source>Elevate Barrier</source>
+ <translation type="finished">以管ç†å“¡èº«ä»½ä½¿ç”¨ Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SettingsDialog.cpp" line="152"/>
+ <source>Are you sure you want to elevate Barrier?
+
+This allows Barrier to interact with elevated processes and the UAC dialog, but can cause problems with non-elevated processes. Elevate Barrier only if you really need to.</source>
+ <translation type="finished">你是å¦è‚¯å®šä»¥ç®¡ç†å“¡èº«ä»½ä½¿ç”¨ Barrier ?
+這將會容許 Barrier 接觸系統程åºåŠ UAC 使用者帳戶控制,但å¯èƒ½æœƒèˆ‡éžç®¡ç†å“¡èº«ä»½åŸ·è¡Œçš„程å¼ç™¼ç”Ÿå•é¡Œã€‚è«‹èªçœŸè€ƒæ…®ã€‚</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialogBase</name>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="14"/>
+ <source>Settings</source>
+ <translation type="finished">設定</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="32"/>
+ <source>Sc&amp;reen name:</source>
+ <translation type="finished">顯示å稱:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="49"/>
+ <source>P&amp;ort:</source>
+ <translation type="finished">端å£</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="78"/>
+ <source>&amp;Interface:</source>
+ <translation type="finished">網絡界é¢ï¼š</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="120"/>
+ <source>Elevate mode</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="127"/>
+ <source>&amp;Hide on startup</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="146"/>
+ <source>&amp;Network Security</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="155"/>
+ <source>Use &amp;SSL encryption (unique certificate)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="184"/>
+ <source>Logging</source>
+ <translation type="finished">記錄日誌</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="202"/>
+ <source>&amp;Logging level:</source>
+ <translation type="finished">記錄等級:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="251"/>
+ <source>Log to file:</source>
+ <translation type="finished">記錄至檔案:</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="268"/>
+ <source>Browse...</source>
+ <translation type="finished">ç€è¦½...</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="213"/>
+ <source>Error</source>
+ <translation type="finished">錯誤</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="107"/>
+ <source>&amp;Language:</source>
+ <translation type="finished">語言</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="20"/>
+ <source>&amp;Miscellaneous</source>
+ <translation type="finished">&amp;雜項</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="218"/>
+ <source>Warning</source>
+ <translation type="finished">警告</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="223"/>
+ <source>Note</source>
+ <translation type="finished">通知</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="228"/>
+ <source>Info</source>
+ <translation type="finished">訊æ¯</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="233"/>
+ <source>Debug</source>
+ <translation type="finished">åµéŒ¯</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="238"/>
+ <source>Debug1</source>
+ <translation type="finished">åµéŒ¯1</translation>
+ </message>
+ <message>
+ <location filename="res/SettingsDialogBase.ui" line="243"/>
+ <source>Debug2</source>
+ <translation type="finished">åµéŒ¯2</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizard</name>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="72"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">設定Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="113"/>
+ <source>Please select an option.</source>
+ <translation type="finished">請挑é¸ä¸€é¸é …。</translation>
+ </message>
+ <message>
+ <location filename="src/SetupWizard.cpp" line="80"/>
+ <source>Please enter your email address and password.</source>
+ <translation type="finished">請輸入你的email和密碼</translation>
+ </message>
+</context>
+<context>
+ <name>SetupWizardBase</name>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="26"/>
+ <source>Setup Barrier</source>
+ <translation type="finished">設定Barrier</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="30"/>
+ <source>Welcome</source>
+ <translation type="finished">æ­¡è¿Ž</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="39"/>
+ <source>Thanks for installing Barrier!</source>
+ <translation type="finished">æ„Ÿè¬æ‚¨å®‰è£Barrier!</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="114"/>
+ <source>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</source>
+ <translation type="finished">Barrier是能讓您於多å°é›»è…¦ä¹‹é–“共享一組éµç›¤èˆ‡æ»‘鼠的自由ã€é–‹æºè»Ÿé«”。您åªé ˆå°‡æ»‘鼠移出電腦螢幕邊緣就能éŠèµ°æ–¼ä¸åŒé›»è…¦ä¹‹é–“,甚至能共用æ¯å°é›»è…¦çš„系統剪貼簿。這些功能僅需ä¾é ä¸€æ¢ç¶²è·¯ç·šé€£çµï¼Œè€Œä¸”Barrier是跨平å°çš„(å¯æ–¼Windowsã€Mac OS XåŠLinux上執行)。</translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="125"/>
+ <source>Activate</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="131"/>
+ <source>&amp;Activate now...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="152"/>
+ <source>Email:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="178"/>
+ <source>Password:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="204"/>
+ <source>&lt;a href=&quot;https://symless.com/account/reset/&quot;&gt;Forgot password&lt;/a&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="232"/>
+ <source>&amp;Skip activation</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="277"/>
+ <source>&amp;Server (share this computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="290"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="326"/>
+ <source>&amp;Client (use another computer's mouse and keyboard)</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="339"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="res/SetupWizardBase.ui" line="262"/>
+ <source>Server or Client?</source>
+ <translation type="finished">伺æœå™¨ç«¯æˆ–客戶端?</translation>
+ </message>
+</context>
+<context>
+ <name>SslCertificate</name>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="42"/>
+ <source>Failed to get profile directory.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="141"/>
+ <source>SSL certificate generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="170"/>
+ <source>SSL fingerprint generated.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/SslCertificate.cpp" line="173"/>
+ <source>Failed to find SSL fingerprint.</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>VersionChecker</name>
+ <message>
+ <location filename="src/VersionChecker.cpp" line="102"/>
+ <source>Unknown</source>
+ <translation type="finished">未知</translation>
+ </message>
+</context>
+<context>
+ <name>WebClient</name>
+ <message>
+ <location filename="src/WebClient.cpp" line="44"/>
+ <source>An error occurred while trying to sign in. Please contact the helpdesk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="65"/>
+ <source>Login failed, invalid email or password.</source>
+ <translation type="finished">登入失敗,無效的email或密碼</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="78"/>
+ <source>Login failed, an error occurred.
+
+%1</source>
+ <translation type="finished">登入失敗,錯誤。
+
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="86"/>
+ <source>Login failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="finished">登入失敗,錯誤。
+
+伺æœå™¨ï¼š
+
+%1</translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="101"/>
+ <source>An error occurred while trying to query the plugin list. Please contact the help desk, and provide the following details.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="120"/>
+ <source>Get plugin list failed, invalid user email or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="131"/>
+ <source>Get plugin list failed, an error occurred.
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/WebClient.cpp" line="137"/>
+ <source>Get plugin list failed, an error occurred.
+
+Server response:
+
+%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>ZeroconfService</name>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="82"/>
+ <source>zeroconf server detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="91"/>
+ <source>zeroconf client detected: %1</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="99"/>
+ <location filename="src/ZeroconfService.cpp" line="130"/>
+ <source>Zero configuration service</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="100"/>
+ <source>Error code: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="131"/>
+ <source>Unable to start the zeroconf: %1.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="140"/>
+ <source>Barrier</source>
+ <translation type="finished">Barrier</translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="141"/>
+ <source>Failed to get local IP address. Please manually type in server address on your clients</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="src/ZeroconfService.cpp" line="147"/>
+ <location filename="src/ZeroconfService.cpp" line="154"/>
+ <source>%1</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/src/gui/res/mac/Info.plist b/src/gui/res/mac/Info.plist
new file mode 100644
index 0000000..d48d7c6
--- /dev/null
+++ b/src/gui/res/mac/Info.plist
@@ -0,0 +1,28 @@
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleDisplayName</key>
+ <string>Barrier</string>
+ <key>CFBundleExecutable</key>
+ <string>Barrier</string>
+ <key>CFBundleIconFile</key>
+ <string>Barrier.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>barrier</string>
+ <!-- TODO: Fix this in v2.0 //-->
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>Barrier</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.8.8</string>
+ <key>CFBundleVersion</key>
+ <string>1.8.8</string>
+ <key>NSHumanReadableCopyright</key>
+ <string>© 2012-2016, Symless Ltd</string>
+ </dict>
+</plist>
diff --git a/src/gui/res/mac/QBarrier.icns b/src/gui/res/mac/QBarrier.icns
new file mode 100644
index 0000000..0d87c5a
--- /dev/null
+++ b/src/gui/res/mac/QBarrier.icns
Binary files differ
diff --git a/src/gui/res/win/Barrier.rc b/src/gui/res/win/Barrier.rc
new file mode 100644
index 0000000..9db2b3e
--- /dev/null
+++ b/src/gui/res/win/Barrier.rc
@@ -0,0 +1 @@
+IDI_ICON1 ICON DISCARDABLE "../icons/256x256/barrier.ico"
diff --git a/src/gui/src/AboutDialog.cpp b/src/gui/src/AboutDialog.cpp
new file mode 100644
index 0000000..76ba26b
--- /dev/null
+++ b/src/gui/src/AboutDialog.cpp
@@ -0,0 +1,55 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "AboutDialog.h"
+
+#include <QtCore>
+#include <QtGui>
+
+AboutDialog::AboutDialog(QWidget* parent, const QString& barrierApp) :
+ QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
+ Ui::AboutDialogBase()
+{
+ setupUi(this);
+
+ m_versionChecker.setApp(barrierApp);
+ QString version = m_versionChecker.getVersion();
+ version = version + '-' + BARRIER_VERSION_STAGE;
+#ifdef BARRIER_REVISION
+ version += '-';
+ version += BARRIER_REVISION;
+#endif
+ m_pLabelBarrierVersion->setText(version);
+
+ QString buildDateString = QString::fromLocal8Bit(__DATE__).simplified();
+ QDate buildDate = QLocale("en_US").toDate(buildDateString, "MMM d yyyy");
+ m_pLabelBuildDate->setText(buildDate.toString(Qt::SystemLocaleLongDate));
+
+ // change default size based on os
+#if defined(Q_OS_MAC)
+ QSize size(600, 380);
+ setMaximumSize(size);
+ setMinimumSize(size);
+ resize(size);
+#elif defined(Q_OS_LINUX)
+ QSize size(600, 330);
+ setMaximumSize(size);
+ setMinimumSize(size);
+ resize(size);
+#endif
+}
diff --git a/src/gui/src/AboutDialog.h b/src/gui/src/AboutDialog.h
new file mode 100644
index 0000000..3c498b4
--- /dev/null
+++ b/src/gui/src/AboutDialog.h
@@ -0,0 +1,43 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(ABOUTDIALOG__H)
+
+#define ABOUTDIALOG__H
+
+#include <QDialog>
+#include "VersionChecker.h"
+
+#include "ui_AboutDialogBase.h"
+
+class QWidget;
+class QString;
+
+class AboutDialog : public QDialog, public Ui::AboutDialogBase
+{
+ Q_OBJECT
+
+ public:
+ AboutDialog(QWidget* parent, const QString& barrierApp = QString());
+
+ private:
+ VersionChecker m_versionChecker;
+};
+
+#endif
+
diff --git a/src/gui/src/AboutDialogBase.ui b/src/gui/src/AboutDialogBase.ui
new file mode 100644
index 0000000..96fee15
--- /dev/null
+++ b/src/gui/src/AboutDialogBase.ui
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AboutDialogBase</class>
+ <widget class="QDialog" name="AboutDialogBase">
+ <property name="windowModality">
+ <enum>Qt::ApplicationModal</enum>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>450</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>450</width>
+ <height>300</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>450</width>
+ <height>300</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>About Barrier</string>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <layout class="QGridLayout">
+ <item row="2" column="1" colspan="2">
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>&lt;p&gt;
+Keyboard and mouse sharing application. Cross platform and open source.&lt;br /&gt;&lt;br /&gt;
+Copyright © 2018 Debauchee Open Source Group&lt;br /&gt;
+Copyright © 2012-2016 Symless Ltd.&lt;br /&gt;
+Copyright © 2002-2012 Chris Schoeneman, Nick Bolton, Volker Lanz.&lt;br /&gt;&lt;br /&gt;
+Barrier is released under the GNU General Public License (GPLv2).&lt;br /&gt;&lt;br /&gt;
+Barrier is based on CosmoSynergy by Richard Lee and Adam Feder.&lt;br /&gt;
+The Barrier GUI is based on QSynergy by Volker Lanz.
+&lt;/p&gt;</string>
+ </property>
+ <property name="margin">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="2">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Preferred</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>100</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="Barrier.qrc">:/res/image/about.png</pixmap>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" colspan="2">
+ <spacer name="spacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>5</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="5" column="1" colspan="2">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Version:</string>
+ </property>
+ <property name="margin">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_pLabelBarrierVersion">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Unknown</string>
+ </property>
+ <property name="margin">
+ <number>1</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="6" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Build Date: </string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_pLabelBuildDate">
+ <property name="text">
+ <string>Unknown</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonOk">
+ <property name="text">
+ <string>&amp;Ok</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="Barrier.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>AboutDialogBase</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>315</x>
+ <y>374</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>301</x>
+ <y>3</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/src/Action.cpp b/src/gui/src/Action.cpp
new file mode 100644
index 0000000..2e0f339
--- /dev/null
+++ b/src/gui/src/Action.cpp
@@ -0,0 +1,150 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "Action.h"
+
+#include <QSettings>
+#include <QTextStream>
+
+const char* Action::m_ActionTypeNames[] =
+{
+ "keyDown", "keyUp", "keystroke",
+ "switchToScreen", "switchInDirection", "lockCursorToScreen",
+ "mouseDown", "mouseUp", "mousebutton"
+};
+
+const char* Action::m_SwitchDirectionNames[] = { "left", "right", "up", "down" };
+const char* Action::m_LockCursorModeNames[] = { "toggle", "on", "off" };
+
+Action::Action() :
+ m_KeySequence(),
+ m_Type(keystroke),
+ m_TypeScreenNames(),
+ m_SwitchScreenName(),
+ m_SwitchDirection(switchLeft),
+ m_LockCursorMode(lockCursorToggle),
+ m_ActiveOnRelease(false),
+ m_HasScreens(false)
+{
+}
+
+QString Action::text() const
+{
+ QString text = QString(m_ActionTypeNames[keySequence().isMouseButton() ? type() + 6 : type() ]) + "(";
+
+ switch (type())
+ {
+ case keyDown:
+ case keyUp:
+ case keystroke:
+ {
+ text += keySequence().toString();
+
+ if (!keySequence().isMouseButton())
+ {
+ const QStringList& screens = typeScreenNames();
+ if (haveScreens() && !screens.isEmpty())
+ {
+ text += ",";
+
+ for (int i = 0; i < screens.size(); i++)
+ {
+ text += screens[i];
+ if (i != screens.size() - 1)
+ text += ":";
+ }
+ }
+ else
+ text += ",*";
+ }
+ }
+ break;
+
+ case switchToScreen:
+ text += switchScreenName();
+ break;
+
+ case switchInDirection:
+ text += m_SwitchDirectionNames[m_SwitchDirection];
+ break;
+
+ case lockCursorToScreen:
+ text += m_LockCursorModeNames[m_LockCursorMode];
+ break;
+
+ default:
+ Q_ASSERT(0);
+ break;
+ }
+
+ text += ")";
+
+ return text;
+}
+
+void Action::loadSettings(QSettings& settings)
+{
+ keySequence().loadSettings(settings);
+ setType(settings.value("type", keyDown).toInt());
+
+ typeScreenNames().clear();
+ int numTypeScreens = settings.beginReadArray("typeScreenNames");
+ for (int i = 0; i < numTypeScreens; i++)
+ {
+ settings.setArrayIndex(i);
+ typeScreenNames().append(settings.value("typeScreenName").toString());
+ }
+ settings.endArray();
+
+ setSwitchScreenName(settings.value("switchScreenName").toString());
+ setSwitchDirection(settings.value("switchInDirection", switchLeft).toInt());
+ setLockCursorMode(settings.value("lockCursorToScreen", lockCursorToggle).toInt());
+ setActiveOnRelease(settings.value("activeOnRelease", false).toBool());
+ setHaveScreens(settings.value("hasScreens", false).toBool());
+}
+
+void Action::saveSettings(QSettings& settings) const
+{
+ keySequence().saveSettings(settings);
+ settings.setValue("type", type());
+
+ settings.beginWriteArray("typeScreenNames");
+ for (int i = 0; i < typeScreenNames().size(); i++)
+ {
+ settings.setArrayIndex(i);
+ settings.setValue("typeScreenName", typeScreenNames()[i]);
+ }
+ settings.endArray();
+
+ settings.setValue("switchScreenName", switchScreenName());
+ settings.setValue("switchInDirection", switchDirection());
+ settings.setValue("lockCursorToScreen", lockCursorMode());
+ settings.setValue("activeOnRelease", activeOnRelease());
+ settings.setValue("hasScreens", haveScreens());
+}
+
+QTextStream& operator<<(QTextStream& outStream, const Action& action)
+{
+ if (action.activeOnRelease())
+ outStream << ";";
+
+ outStream << action.text();
+
+ return outStream;
+}
+
diff --git a/src/gui/src/Action.h b/src/gui/src/Action.h
new file mode 100644
index 0000000..2786842
--- /dev/null
+++ b/src/gui/src/Action.h
@@ -0,0 +1,89 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(ACTION_H)
+
+#define ACTION_H
+
+#include "KeySequence.h"
+
+#include <QString>
+#include <QStringList>
+#include <QList>
+
+class ActionDialog;
+class QSettings;
+class QTextStream;
+
+class Action
+{
+ friend class ActionDialog;
+ friend QTextStream& operator<<(QTextStream& outStream, const Action& action);
+
+ public:
+ enum ActionType { keyDown, keyUp, keystroke, switchToScreen, switchInDirection, lockCursorToScreen, mouseDown, mouseUp, mousebutton };
+ enum SwitchDirection { switchLeft, switchRight, switchUp, switchDown };
+ enum LockCursorMode { lockCursorToggle, lockCursonOn, lockCursorOff };
+
+ public:
+ Action();
+
+ public:
+ QString text() const;
+ const KeySequence& keySequence() const { return m_KeySequence; }
+ void loadSettings(QSettings& settings);
+ void saveSettings(QSettings& settings) const;
+ int type() const { return m_Type; }
+ const QStringList& typeScreenNames() const { return m_TypeScreenNames; }
+ const QString& switchScreenName() const { return m_SwitchScreenName; }
+ int switchDirection() const { return m_SwitchDirection; }
+ int lockCursorMode() const { return m_LockCursorMode; }
+ bool activeOnRelease() const { return m_ActiveOnRelease; }
+ bool haveScreens() const { return m_HasScreens; }
+
+ protected:
+ KeySequence& keySequence() { return m_KeySequence; }
+ void setKeySequence(const KeySequence& seq) { m_KeySequence = seq; }
+ void setType(int t) { m_Type = t; }
+ QStringList& typeScreenNames() { return m_TypeScreenNames; }
+ void setSwitchScreenName(const QString& n) { m_SwitchScreenName = n; }
+ void setSwitchDirection(int d) { m_SwitchDirection = d; }
+ void setLockCursorMode(int m) { m_LockCursorMode = m; }
+ void setActiveOnRelease(bool b) { m_ActiveOnRelease = b; }
+ void setHaveScreens(bool b) { m_HasScreens = b; }
+
+ private:
+ KeySequence m_KeySequence;
+ int m_Type;
+ QStringList m_TypeScreenNames;
+ QString m_SwitchScreenName;
+ int m_SwitchDirection;
+ int m_LockCursorMode;
+ bool m_ActiveOnRelease;
+ bool m_HasScreens;
+
+ static const char* m_ActionTypeNames[];
+ static const char* m_SwitchDirectionNames[];
+ static const char* m_LockCursorModeNames[];
+};
+
+typedef QList<Action> ActionList;
+
+QTextStream& operator<<(QTextStream& outStream, const Action& action);
+
+#endif
diff --git a/src/gui/src/ActionDialog.cpp b/src/gui/src/ActionDialog.cpp
new file mode 100644
index 0000000..e824ea3
--- /dev/null
+++ b/src/gui/src/ActionDialog.cpp
@@ -0,0 +1,109 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "ActionDialog.h"
+
+#include "Hotkey.h"
+#include "Action.h"
+#include "ServerConfig.h"
+#include "KeySequence.h"
+
+#include <QtCore>
+#include <QtGui>
+
+ActionDialog::ActionDialog(QWidget* parent, ServerConfig& config, Hotkey& hotkey, Action& action) :
+ QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
+ Ui::ActionDialogBase(),
+ m_ServerConfig(config),
+ m_Hotkey(hotkey),
+ m_Action(action),
+ m_pButtonGroupType(new QButtonGroup(this))
+{
+ setupUi(this);
+
+ // work around Qt Designer's lack of a QButtonGroup; we need it to get
+ // at the button id of the checked radio button
+ QRadioButton* const typeButtons[] = { m_pRadioPress, m_pRadioRelease, m_pRadioPressAndRelease, m_pRadioSwitchToScreen, m_pRadioSwitchInDirection, m_pRadioLockCursorToScreen };
+
+ for (unsigned int i = 0; i < sizeof(typeButtons) / sizeof(typeButtons[0]); i++)
+ m_pButtonGroupType->addButton(typeButtons[i], i);
+
+ m_pKeySequenceWidgetHotkey->setText(m_Action.keySequence().toString());
+ m_pKeySequenceWidgetHotkey->setKeySequence(m_Action.keySequence());
+ m_pButtonGroupType->button(m_Action.type())->setChecked(true);
+ m_pComboSwitchInDirection->setCurrentIndex(m_Action.switchDirection());
+ m_pComboLockCursorToScreen->setCurrentIndex(m_Action.lockCursorMode());
+
+ if (m_Action.activeOnRelease())
+ m_pRadioHotkeyReleased->setChecked(true);
+ else
+ m_pRadioHotkeyPressed->setChecked(true);
+
+ m_pGroupBoxScreens->setChecked(m_Action.haveScreens());
+
+ int idx = 0;
+ foreach(const Screen& screen, serverConfig().screens())
+ if (!screen.isNull())
+ {
+ QListWidgetItem *pListItem = new QListWidgetItem(screen.name());
+ m_pListScreens->addItem(pListItem);
+ if (m_Action.typeScreenNames().indexOf(screen.name()) != -1)
+ m_pListScreens->setCurrentItem(pListItem);
+
+ m_pComboSwitchToScreen->addItem(screen.name());
+ if (screen.name() == m_Action.switchScreenName())
+ m_pComboSwitchToScreen->setCurrentIndex(idx);
+
+ idx++;
+ }
+}
+
+void ActionDialog::accept()
+{
+ if (!sequenceWidget()->valid() && m_pButtonGroupType->checkedId() >= 0 && m_pButtonGroupType->checkedId() < 3)
+ return;
+
+ m_Action.setKeySequence(sequenceWidget()->keySequence());
+ m_Action.setType(m_pButtonGroupType->checkedId());
+ m_Action.setHaveScreens(m_pGroupBoxScreens->isChecked());
+
+ m_Action.typeScreenNames().clear();
+ foreach(const QListWidgetItem* pItem, m_pListScreens->selectedItems())
+ m_Action.typeScreenNames().append(pItem->text());
+
+ m_Action.setSwitchScreenName(m_pComboSwitchToScreen->currentText());
+ m_Action.setSwitchDirection(m_pComboSwitchInDirection->currentIndex());
+ m_Action.setLockCursorMode(m_pComboLockCursorToScreen->currentIndex());
+ m_Action.setActiveOnRelease(m_pRadioHotkeyReleased->isChecked());
+
+ QDialog::accept();
+}
+
+void ActionDialog::on_m_pKeySequenceWidgetHotkey_keySequenceChanged()
+{
+ if (sequenceWidget()->keySequence().isMouseButton())
+ {
+ m_pGroupBoxScreens->setEnabled(false);
+ m_pListScreens->setEnabled(false);
+ }
+ else
+ {
+ m_pGroupBoxScreens->setEnabled(true);
+ m_pListScreens->setEnabled(true);
+ }
+}
diff --git a/src/gui/src/ActionDialog.h b/src/gui/src/ActionDialog.h
new file mode 100644
index 0000000..388be1f
--- /dev/null
+++ b/src/gui/src/ActionDialog.h
@@ -0,0 +1,56 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(ACTIONDIALOG_H)
+
+#define ACTIONDIALOG_H
+
+#include <QDialog>
+
+#include "ui_ActionDialogBase.h"
+
+class Hotkey;
+class Action;
+class QRadioButton;
+class QButtonGroup;
+class ServerConfig;
+
+class ActionDialog : public QDialog, public Ui::ActionDialogBase
+{
+ Q_OBJECT
+
+ public:
+ ActionDialog(QWidget* parent, ServerConfig& config, Hotkey& hotkey, Action& action);
+
+ protected slots:
+ void accept();
+ void on_m_pKeySequenceWidgetHotkey_keySequenceChanged();
+
+ protected:
+ const KeySequenceWidget* sequenceWidget() const { return m_pKeySequenceWidgetHotkey; }
+ const ServerConfig& serverConfig() const { return m_ServerConfig; }
+
+ private:
+ const ServerConfig& m_ServerConfig;
+ Hotkey& m_Hotkey;
+ Action& m_Action;
+
+ QButtonGroup* m_pButtonGroupType;
+};
+
+#endif
diff --git a/src/gui/src/ActionDialogBase.ui b/src/gui/src/ActionDialogBase.ui
new file mode 100644
index 0000000..f6dff78
--- /dev/null
+++ b/src/gui/src/ActionDialogBase.ui
@@ -0,0 +1,581 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ActionDialogBase</class>
+ <widget class="QDialog" name="ActionDialogBase">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>372</width>
+ <height>484</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Action</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QGroupBox" name="m_pGroupType">
+ <property name="title">
+ <string>Choose the action to perform</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QRadioButton" name="m_pRadioPress">
+ <property name="text">
+ <string>Press a hotkey</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="m_pRadioRelease">
+ <property name="text">
+ <string>Release a hotkey</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="m_pRadioPressAndRelease">
+ <property name="text">
+ <string>Press and release a hotkey</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="KeySequenceWidget" name="m_pKeySequenceWidgetHotkey">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>256</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="m_pGroupBoxScreens">
+ <property name="title">
+ <string>only on these screens</string>
+ </property>
+ <property name="flat">
+ <bool>true</bool>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout">
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QListWidget" name="m_pListScreens">
+ <property name="minimumSize">
+ <size>
+ <width>128</width>
+ <height>64</height>
+ </size>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::ExtendedSelection</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QRadioButton" name="m_pRadioSwitchToScreen">
+ <property name="text">
+ <string>Switch to screen</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QComboBox" name="m_pComboSwitchToScreen">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QRadioButton" name="m_pRadioSwitchInDirection">
+ <property name="text">
+ <string>Switch in direction</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QComboBox" name="m_pComboSwitchInDirection">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <item>
+ <property name="text">
+ <string>left</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>right</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>up</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>down</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QRadioButton" name="m_pRadioLockCursorToScreen">
+ <property name="text">
+ <string>Lock cursor to screen</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QComboBox" name="m_pComboLockCursorToScreen">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <item>
+ <property name="text">
+ <string>toggle</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>on</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>off</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>This action is performed when</string>
+ </property>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QRadioButton" name="m_pRadioHotkeyPressed">
+ <property name="text">
+ <string>the hotkey is pressed</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="m_pRadioHotkeyReleased">
+ <property name="text">
+ <string>the hotkey is released</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>KeySequenceWidget</class>
+ <extends>QLineEdit</extends>
+ <header>KeySequenceWidget.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ActionDialogBase</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>245</x>
+ <y>474</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ActionDialogBase</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>313</x>
+ <y>474</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pGroupType</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pKeySequenceWidgetHotkey</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>104</x>
+ <y>194</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>110</x>
+ <y>132</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioSwitchInDirection</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pKeySequenceWidgetHotkey</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>118</x>
+ <y>322</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>81</x>
+ <y>129</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioLockCursorToScreen</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pKeySequenceWidgetHotkey</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>101</x>
+ <y>353</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>68</x>
+ <y>126</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioPress</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pKeySequenceWidgetHotkey</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>48</x>
+ <y>48</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>45</x>
+ <y>129</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioRelease</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pKeySequenceWidgetHotkey</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>135</x>
+ <y>70</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>148</x>
+ <y>125</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioPressAndRelease</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pKeySequenceWidgetHotkey</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>194</x>
+ <y>100</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>201</x>
+ <y>125</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioSwitchToScreen</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pComboSwitchToScreen</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>148</x>
+ <y>291</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>350</x>
+ <y>290</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioSwitchInDirection</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pComboSwitchInDirection</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>158</x>
+ <y>322</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>350</x>
+ <y>321</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioLockCursorToScreen</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pComboLockCursorToScreen</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>180</x>
+ <y>353</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>350</x>
+ <y>352</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioPress</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pGroupBoxScreens</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>25</x>
+ <y>47</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>33</x>
+ <y>155</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioSwitchToScreen</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pGroupBoxScreens</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>48</x>
+ <y>278</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>98</x>
+ <y>153</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioRelease</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pGroupBoxScreens</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>264</x>
+ <y>67</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>241</x>
+ <y>158</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioPressAndRelease</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pGroupBoxScreens</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>286</x>
+ <y>98</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>290</x>
+ <y>156</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioSwitchInDirection</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pGroupBoxScreens</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>38</x>
+ <y>313</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>64</x>
+ <y>195</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioLockCursorToScreen</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pGroupBoxScreens</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>48</x>
+ <y>339</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>79</x>
+ <y>234</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioSwitchToScreen</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pKeySequenceWidgetHotkey</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>84</x>
+ <y>280</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>185</x>
+ <y>123</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/src/AddClientDialog.cpp b/src/gui/src/AddClientDialog.cpp
new file mode 100644
index 0000000..afa945d
--- /dev/null
+++ b/src/gui/src/AddClientDialog.cpp
@@ -0,0 +1,129 @@
+/*
+ * barrier -- 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 "AddClientDialog.h"
+#include "ui_AddClientDialogBase.h"
+
+#include <QPushButton>
+#include <QLabel>
+
+AddClientDialog::AddClientDialog(const QString& clientName, QWidget* parent) :
+ QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
+ Ui::AddClientDialog(),
+ m_AddResult(kAddClientIgnore),
+ m_IgnoreAutoConfigClient(false)
+{
+ setupUi(this);
+
+ m_pLabelHead->setText("A client wants to connect. "
+ "Please choose a location for " + clientName + ".");
+
+ QIcon icon(":res/icons/64x64/video-display.png");
+ QSize IconSize(32,32);
+
+ m_pButtonLeft = new QPushButton(this);
+ m_pButtonLeft->setIcon(icon);
+ m_pButtonLeft->setIconSize(IconSize);
+ gridLayout->addWidget(m_pButtonLeft, 2, 0, 1, 1, Qt::AlignCenter);
+ connect(m_pButtonLeft, SIGNAL(clicked()), this, SLOT(handleButtonLeft()));
+
+ m_pButtonUp = new QPushButton(this);
+ m_pButtonUp->setIcon(icon);
+ m_pButtonUp->setIconSize(IconSize);
+ gridLayout->addWidget(m_pButtonUp, 1, 1, 1, 1, Qt::AlignCenter);
+ connect(m_pButtonUp, SIGNAL(clicked()), this, SLOT(handleButtonUp()));
+
+ m_pButtonRight = new QPushButton(this);
+ m_pButtonRight->setIcon(icon);
+ m_pButtonRight->setIconSize(IconSize);
+ gridLayout->addWidget(m_pButtonRight, 2, 2, 1, 1, Qt::AlignCenter);
+ connect(m_pButtonRight, SIGNAL(clicked()), this, SLOT(handleButtonRight()));
+
+ m_pButtonDown = new QPushButton(this);
+ m_pButtonDown->setIcon(icon);
+ m_pButtonDown->setIconSize(IconSize);
+ gridLayout->addWidget(m_pButtonDown, 3, 1, 1, 1, Qt::AlignCenter);
+ connect(m_pButtonDown, SIGNAL(clicked()), this, SLOT(handleButtonDown()));
+
+ m_pLabelCenter = new QLabel(this);
+ m_pLabelCenter->setPixmap(QPixmap(":res/icons/64x64/video-display.png"));
+ gridLayout->addWidget(m_pLabelCenter, 2, 1, 1, 1, Qt::AlignCenter);
+
+#if defined(Q_OS_MAC)
+ m_pDialogButtonBox->setLayoutDirection(Qt::RightToLeft);
+#endif
+
+ QPushButton* advanced = m_pDialogButtonBox->addButton("Advanced",
+ QDialogButtonBox::HelpRole);
+ connect(advanced, SIGNAL(clicked()), this, SLOT(handleButtonAdvanced()));
+}
+
+AddClientDialog::~AddClientDialog()
+{
+ delete m_pButtonUp;
+ delete m_pButtonDown;
+ delete m_pButtonLeft;
+ delete m_pButtonRight;
+ delete m_pLabelCenter;
+}
+
+void AddClientDialog::changeEvent(QEvent *e)
+{
+ QDialog::changeEvent(e);
+ switch (e->type()) {
+ case QEvent::LanguageChange:
+ retranslateUi(this);
+ break;
+ default:
+ break;
+ }
+}
+
+void AddClientDialog::handleButtonLeft()
+{
+ m_AddResult = kAddClientLeft;
+ close();
+}
+
+void AddClientDialog::handleButtonUp()
+{
+ m_AddResult = kAddClientUp;
+ close();
+}
+
+void AddClientDialog::handleButtonRight()
+{
+ m_AddResult = kAddClientRight;
+ close();
+}
+
+void AddClientDialog::handleButtonDown()
+{
+ m_AddResult = kAddClientDown;
+ close();
+}
+
+void AddClientDialog::handleButtonAdvanced()
+{
+ m_AddResult = kAddClientOther;
+ close();
+}
+
+void AddClientDialog::on_m_pCheckBoxIgnoreClient_toggled(bool checked)
+{
+ m_IgnoreAutoConfigClient = checked;
+}
diff --git a/src/gui/src/AddClientDialog.h b/src/gui/src/AddClientDialog.h
new file mode 100644
index 0000000..6d49cee
--- /dev/null
+++ b/src/gui/src/AddClientDialog.h
@@ -0,0 +1,68 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#ifndef ADDCLIENTDIALOG_H
+#define ADDCLIENTDIALOG_H
+
+#include "ui_AddClientDialogBase.h"
+
+#include <QDialog>
+
+class QPushButton;
+class QLabel;
+
+enum {
+ kAddClientRight,
+ kAddClientLeft,
+ kAddClientUp,
+ kAddClientDown,
+ kAddClientOther,
+ kAddClientIgnore
+};
+
+class AddClientDialog : public QDialog, public Ui::AddClientDialog
+{
+ Q_OBJECT
+public:
+ AddClientDialog(const QString& clientName, QWidget* parent = 0);
+ ~AddClientDialog();
+
+ int addResult() { return m_AddResult; }
+ bool ignoreAutoConfigClient() { return m_IgnoreAutoConfigClient; }
+
+protected:
+ void changeEvent(QEvent *e);
+
+private slots:
+ void on_m_pCheckBoxIgnoreClient_toggled(bool checked);
+ void handleButtonLeft();
+ void handleButtonUp();
+ void handleButtonRight();
+ void handleButtonDown();
+ void handleButtonAdvanced();
+
+private:
+ QPushButton* m_pButtonLeft;
+ QPushButton* m_pButtonUp;
+ QPushButton* m_pButtonRight;
+ QPushButton* m_pButtonDown;
+ QLabel* m_pLabelCenter;
+ int m_AddResult;
+ bool m_IgnoreAutoConfigClient;
+};
+
+#endif // ADDCLIENTDIALOG_H
diff --git a/src/gui/src/AddClientDialogBase.ui b/src/gui/src/AddClientDialogBase.ui
new file mode 100644
index 0000000..24397b4
--- /dev/null
+++ b/src/gui/src/AddClientDialogBase.ui
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>AddClientDialog</class>
+ <widget class="QDialog" name="AddClientDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>350</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <widget class="QWidget" name="gridLayoutWidget">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>10</y>
+ <width>381</width>
+ <height>301</height>
+ </rect>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="1">
+ <widget class="QLabel" name="m_pLabelHead">
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="horizontalLayoutWidget">
+ <property name="geometry">
+ <rect>
+ <x>10</x>
+ <y>320</y>
+ <width>381</width>
+ <height>31</height>
+ </rect>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QCheckBox" name="m_pCheckBoxIgnoreClient">
+ <property name="text">
+ <string>Ignore auto connect clients</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="m_pDialogButtonBox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Ignore</set>
+ </property>
+ <property name="centerButtons">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>m_pDialogButtonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>AddClientDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pDialogButtonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>AddClientDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/src/AppConfig.cpp b/src/gui/src/AppConfig.cpp
new file mode 100644
index 0000000..2f8779d
--- /dev/null
+++ b/src/gui/src/AppConfig.cpp
@@ -0,0 +1,231 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "AppConfig.h"
+#include "QUtility.h"
+
+#include <QtCore>
+#include <QtNetwork>
+
+#if defined(Q_OS_WIN)
+const char AppConfig::m_BarriersName[] = "barriers.exe";
+const char AppConfig::m_BarriercName[] = "barrierc.exe";
+const char AppConfig::m_BarrierLogDir[] = "log/";
+#define DEFAULT_PROCESS_MODE Service
+#else
+const char AppConfig::m_BarriersName[] = "barriers";
+const char AppConfig::m_BarriercName[] = "barrierc";
+const char AppConfig::m_BarrierLogDir[] = "/var/log/";
+#define DEFAULT_PROCESS_MODE Desktop
+#endif
+
+const ElevateMode defaultElevateMode = ElevateAsNeeded;
+
+static const char* logLevelNames[] =
+{
+ "ERROR",
+ "WARNING",
+ "NOTE",
+ "INFO",
+ "DEBUG",
+ "DEBUG1",
+ "DEBUG2"
+};
+
+AppConfig::AppConfig(QSettings* settings) :
+ m_pSettings(settings),
+ m_ScreenName(),
+ m_Port(24800),
+ m_Interface(),
+ m_LogLevel(0),
+ m_WizardLastRun(0),
+ m_ProcessMode(DEFAULT_PROCESS_MODE),
+ m_AutoConfig(true),
+ m_ElevateMode(defaultElevateMode),
+ m_AutoConfigPrompted(false),
+ m_CryptoEnabled(false),
+ m_AutoHide(false),
+ m_MinimizeToTray(false)
+{
+ Q_ASSERT(m_pSettings);
+
+ loadSettings();
+}
+
+AppConfig::~AppConfig()
+{
+ saveSettings();
+}
+
+const QString &AppConfig::screenName() const { return m_ScreenName; }
+
+int AppConfig::port() const { return m_Port; }
+
+const QString &AppConfig::networkInterface() const { return m_Interface; }
+
+int AppConfig::logLevel() const { return m_LogLevel; }
+
+bool AppConfig::logToFile() const { return m_LogToFile; }
+
+const QString &AppConfig::logFilename() const { return m_LogFilename; }
+
+QString AppConfig::barrierLogDir() const
+{
+#if defined(Q_OS_WIN)
+ // on windows, we want to log to program files
+ return barrierProgramDir() + "log/";
+#else
+ // on unix, we'll log to the standard log dir
+ return "/var/log/";
+#endif
+}
+
+QString AppConfig::barrierProgramDir() const
+{
+ // barrier binaries should be in the same dir.
+ return QCoreApplication::applicationDirPath() + "/";
+}
+
+void AppConfig::persistLogDir()
+{
+ QDir dir = barrierLogDir();
+
+ // persist the log directory
+ if (!dir.exists())
+ {
+ dir.mkpath(dir.path());
+ }
+}
+
+const QString AppConfig::logFilenameCmd() const
+{
+ QString filename = m_LogFilename;
+#if defined(Q_OS_WIN)
+ // wrap in quotes in case username contains spaces.
+ filename = QString("\"%1\"").arg(filename);
+#endif
+ return filename;
+}
+
+QString AppConfig::logLevelText() const
+{
+ return logLevelNames[logLevel()];
+}
+
+ProcessMode AppConfig::processMode() const { return m_ProcessMode; }
+
+bool AppConfig::wizardShouldRun() const { return m_WizardLastRun < kWizardVersion; }
+
+const QString &AppConfig::language() const { return m_Language; }
+
+bool AppConfig::startedBefore() const { return m_StartedBefore; }
+
+bool AppConfig::autoConfig() const { return m_AutoConfig; }
+
+void AppConfig::loadSettings()
+{
+ m_ScreenName = settings().value("screenName", QHostInfo::localHostName()).toString();
+ m_Port = settings().value("port", 24800).toInt();
+ m_Interface = settings().value("interface").toString();
+ m_LogLevel = settings().value("logLevel", 3).toInt(); // level 3: INFO
+ m_LogToFile = settings().value("logToFile", false).toBool();
+ m_LogFilename = settings().value("logFilename", barrierLogDir() + "barrier.log").toString();
+ m_WizardLastRun = settings().value("wizardLastRun", 0).toInt();
+ m_Language = settings().value("language", QLocale::system().name()).toString();
+ m_StartedBefore = settings().value("startedBefore", false).toBool();
+ m_AutoConfig = settings().value("autoConfig", true).toBool();
+ QVariant elevateMode = settings().value("elevateModeEnum");
+ if (!elevateMode.isValid()) {
+ elevateMode = settings().value ("elevateMode",
+ QVariant(static_cast<int>(defaultElevateMode)));
+ }
+ m_ElevateMode = static_cast<ElevateMode>(elevateMode.toInt());
+ m_AutoConfigPrompted = settings().value("autoConfigPrompted", false).toBool();
+ m_CryptoEnabled = settings().value("cryptoEnabled", true).toBool();
+ m_AutoHide = settings().value("autoHide", false).toBool();
+ m_MinimizeToTray = settings().value("minimizeToTray", false).toBool();
+}
+
+void AppConfig::saveSettings()
+{
+ settings().setValue("screenName", m_ScreenName);
+ settings().setValue("port", m_Port);
+ settings().setValue("interface", m_Interface);
+ settings().setValue("logLevel", m_LogLevel);
+ settings().setValue("logToFile", m_LogToFile);
+ settings().setValue("logFilename", m_LogFilename);
+ settings().setValue("wizardLastRun", kWizardVersion);
+ settings().setValue("language", m_Language);
+ settings().setValue("startedBefore", m_StartedBefore);
+ settings().setValue("autoConfig", m_AutoConfig);
+ // Refer to enum ElevateMode declaration for insight in to why this
+ // flag is mapped this way
+ settings().setValue("elevateMode", m_ElevateMode == ElevateAlways);
+ settings().setValue("elevateModeEnum", static_cast<int>(m_ElevateMode));
+ settings().setValue("autoConfigPrompted", m_AutoConfigPrompted);
+ settings().setValue("cryptoEnabled", m_CryptoEnabled);
+ settings().setValue("autoHide", m_AutoHide);
+ settings().setValue("minimizeToTray", m_MinimizeToTray);
+ settings().sync();
+}
+
+QSettings &AppConfig::settings() { return *m_pSettings; }
+
+void AppConfig::setScreenName(const QString &s) { m_ScreenName = s; }
+
+void AppConfig::setPort(int i) { m_Port = i; }
+
+void AppConfig::setNetworkInterface(const QString &s) { m_Interface = s; }
+
+void AppConfig::setLogLevel(int i) { m_LogLevel = i; }
+
+void AppConfig::setLogToFile(bool b) { m_LogToFile = b; }
+
+void AppConfig::setLogFilename(const QString &s) { m_LogFilename = s; }
+
+void AppConfig::setWizardHasRun() { m_WizardLastRun = kWizardVersion; }
+
+void AppConfig::setLanguage(const QString language) { m_Language = language; }
+
+void AppConfig::setStartedBefore(bool b) { m_StartedBefore = b; }
+
+void AppConfig::setElevateMode(ElevateMode em) { m_ElevateMode = em; }
+
+void AppConfig::setAutoConfig(bool autoConfig) { m_AutoConfig = autoConfig; }
+
+bool AppConfig::autoConfigPrompted() { return m_AutoConfigPrompted; }
+
+void AppConfig::setAutoConfigPrompted(bool prompted) { m_AutoConfigPrompted = prompted; }
+
+QString AppConfig::barriersName() const { return m_BarriersName; }
+
+QString AppConfig::barriercName() const { return m_BarriercName; }
+
+ElevateMode AppConfig::elevateMode() { return m_ElevateMode; }
+
+void AppConfig::setCryptoEnabled(bool e) { m_CryptoEnabled = e; }
+
+bool AppConfig::getCryptoEnabled() const { return m_CryptoEnabled; }
+
+void AppConfig::setAutoHide(bool b) { m_AutoHide = b; }
+
+bool AppConfig::getAutoHide() { return m_AutoHide; }
+
+void AppConfig::setMinimizeToTray(bool b) { m_MinimizeToTray = b; }
+
+bool AppConfig::getMinimizeToTray() { return m_MinimizeToTray; }
diff --git a/src/gui/src/AppConfig.h b/src/gui/src/AppConfig.h
new file mode 100644
index 0000000..c9ed38d
--- /dev/null
+++ b/src/gui/src/AppConfig.h
@@ -0,0 +1,140 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(APPCONFIG_H)
+
+#define APPCONFIG_H
+
+#include <QObject>
+#include <QString>
+#include "ElevateMode.h"
+
+// this should be incremented each time a new page is added. this is
+// saved to settings when the user finishes running the wizard. if
+// the saved wizard version is lower than this number, the wizard
+// will be displayed. each version incrememnt should be described
+// here...
+//
+// 1: first version
+// 2: added language page
+// 3: added premium page and removed
+// 4: ssl plugin 'ns' v1.0
+// 5: ssl plugin 'ns' v1.1
+// 6: ssl plugin 'ns' v1.2
+// 7: serial key activation
+// 8: Visual Studio 2015 support
+// 9: synergy->barrier and de-commercialized
+//
+const int kWizardVersion = 9;
+
+class QSettings;
+class SettingsDialog;
+
+enum ProcessMode {
+ Service,
+ Desktop
+};
+
+class AppConfig: public QObject
+{
+ Q_OBJECT
+
+ friend class SettingsDialog;
+ friend class MainWindow;
+ friend class SetupWizard;
+
+ public:
+ AppConfig(QSettings* settings);
+ ~AppConfig();
+
+ public:
+ const QString& screenName() const;
+ int port() const;
+ const QString& networkInterface() const;
+ int logLevel() const;
+ bool logToFile() const;
+ const QString& logFilename() const;
+ const QString logFilenameCmd() const;
+ QString logLevelText() const;
+ ProcessMode processMode() const;
+ bool wizardShouldRun() const;
+ const QString& language() const;
+ bool startedBefore() const;
+ bool autoConfig() const;
+ void setAutoConfig(bool autoConfig);
+ bool autoConfigPrompted();
+ void setAutoConfigPrompted(bool prompted);
+
+ QString barriersName() const;
+ QString barriercName() const;
+ QString barrierProgramDir() const;
+ QString barrierLogDir() const;
+
+ void persistLogDir();
+ ElevateMode elevateMode();
+
+ void setCryptoEnabled(bool e);
+ bool getCryptoEnabled() const;
+
+ void setAutoHide(bool b);
+ bool getAutoHide();
+
+ void setMinimizeToTray(bool b);
+ bool getMinimizeToTray();
+
+ void saveSettings();
+
+protected:
+ QSettings& settings();
+ void setScreenName(const QString& s);
+ void setPort(int i);
+ void setNetworkInterface(const QString& s);
+ void setLogLevel(int i);
+ void setLogToFile(bool b);
+ void setLogFilename(const QString& s);
+ void setWizardHasRun();
+ void setLanguage(const QString language);
+ void setStartedBefore(bool b);
+ void setElevateMode(ElevateMode em);
+ void loadSettings();
+
+ private:
+ QSettings* m_pSettings;
+ QString m_ScreenName;
+ int m_Port;
+ QString m_Interface;
+ int m_LogLevel;
+ bool m_LogToFile;
+ QString m_LogFilename;
+ int m_WizardLastRun;
+ ProcessMode m_ProcessMode;
+ QString m_Language;
+ bool m_StartedBefore;
+ bool m_AutoConfig;
+ ElevateMode m_ElevateMode;
+ bool m_AutoConfigPrompted;
+ bool m_CryptoEnabled;
+ bool m_AutoHide;
+ bool m_MinimizeToTray;
+
+ static const char m_BarriersName[];
+ static const char m_BarriercName[];
+ static const char m_BarrierLogDir[];
+};
+
+#endif
diff --git a/src/gui/src/BarrierLocale.cpp b/src/gui/src/BarrierLocale.cpp
new file mode 100644
index 0000000..c9785c5
--- /dev/null
+++ b/src/gui/src/BarrierLocale.cpp
@@ -0,0 +1,68 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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 "BarrierLocale.h"
+
+#include <QResource>
+#include <QXmlStreamReader>
+#include <QDebug>
+
+BarrierLocale::BarrierLocale()
+{
+ loadLanguages();
+}
+
+void BarrierLocale::loadLanguages()
+{
+ QResource resource(":/res/lang/Languages.xml");
+ QByteArray bytes(reinterpret_cast<const char*>(resource.data()), resource.size());
+ QXmlStreamReader xml(bytes);
+
+ while (!xml.atEnd())
+ {
+ QXmlStreamReader::TokenType token = xml.readNext();
+ if (xml.hasError())
+ {
+ qCritical() << xml.errorString();
+ throw std::exception();
+ }
+
+ if (xml.name() == "language" && token == QXmlStreamReader::StartElement)
+ {
+ QXmlStreamAttributes attributes = xml.attributes();
+ addLanguage(
+ attributes.value("ietfCode").toString(),
+ attributes.value("name").toString());
+ }
+ }
+}
+
+void BarrierLocale::addLanguage(const QString& ietfCode, const QString& name)
+{
+ m_Languages.push_back(BarrierLocale::Language(ietfCode, name));
+}
+
+void BarrierLocale::fillLanguageComboBox(QComboBox* comboBox)
+{
+ comboBox->blockSignals(true);
+ QVector<BarrierLocale::Language>::iterator it;
+ for (it = m_Languages.begin(); it != m_Languages.end(); ++it)
+ {
+ comboBox->addItem((*it).m_Name, (*it).m_IetfCode);
+ }
+ comboBox->blockSignals(false);
+}
diff --git a/src/gui/src/BarrierLocale.h b/src/gui/src/BarrierLocale.h
new file mode 100644
index 0000000..a6454ea
--- /dev/null
+++ b/src/gui/src/BarrierLocale.h
@@ -0,0 +1,48 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#include <QString>
+#include <QVector>
+#include <QComboBox>
+
+class BarrierLocale
+{
+ class Language
+ {
+ public:
+ Language() { }
+ Language(const QString& IetfCode, const QString& name)
+ : m_IetfCode(IetfCode), m_Name(name) { }
+
+ public:
+ QString m_IetfCode;
+ QString m_Name;
+ };
+
+public:
+ BarrierLocale();
+ void fillLanguageComboBox(QComboBox* comboBox);
+
+private:
+ void loadLanguages();
+ void addLanguage(const QString& IetfCode, const QString& name);
+
+private:
+ QVector<Language> m_Languages;
+};
diff --git a/src/gui/src/BaseConfig.cpp b/src/gui/src/BaseConfig.cpp
new file mode 100644
index 0000000..36fe430
--- /dev/null
+++ b/src/gui/src/BaseConfig.cpp
@@ -0,0 +1,46 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "BaseConfig.h"
+
+const char* BaseConfig::m_ModifierNames[] =
+{
+ "shift",
+ "ctrl",
+ "alt",
+ "meta",
+ "super",
+ "none"
+};
+
+const char* BaseConfig::m_FixNames[] =
+{
+ "halfDuplexCapsLock",
+ "halfDuplexNumLock",
+ "halfDuplexScrollLock",
+ "xtestIsXineramaUnaware"
+};
+
+const char* BaseConfig::m_SwitchCornerNames[] =
+{
+ "top-left",
+ "top-right",
+ "bottom-left",
+ "bottom-right"
+};
+
diff --git a/src/gui/src/BaseConfig.h b/src/gui/src/BaseConfig.h
new file mode 100644
index 0000000..f1769bb
--- /dev/null
+++ b/src/gui/src/BaseConfig.h
@@ -0,0 +1,91 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(BASECONFIG_H)
+
+#define BASECONFIG_H
+
+#include <QSettings>
+#include <QString>
+#include <QVariant>
+
+class BaseConfig
+{
+ public:
+ enum Modifier { DefaultMod = -1, Shift, Ctrl, Alt, Meta, Super, None, NumModifiers };
+ enum SwitchCorner { TopLeft, TopRight, BottomLeft, BottomRight, NumSwitchCorners };
+ enum Fix { CapsLock, NumLock, ScrollLock, XTest, NumFixes };
+
+ protected:
+ BaseConfig() {}
+ virtual ~BaseConfig() {}
+
+ protected:
+ template<typename T1, typename T2>
+ void readSettings(QSettings& settings, T1& array, const QString& arrayName, const T2& deflt)
+ {
+ int entries = settings.beginReadArray(arrayName + "Array");
+ array.clear();
+ for (int i = 0; i < entries; i++)
+ {
+ settings.setArrayIndex(i);
+ QVariant v = settings.value(arrayName, deflt);
+ array.append(v.value<T2>());
+ }
+ settings.endArray();
+ }
+
+ template<typename T1, typename T2>
+ void readSettings(QSettings& settings, T1& array, const QString& arrayName, const T2& deflt, int entries)
+ {
+ Q_ASSERT(array.size() >= entries);
+ settings.beginReadArray(arrayName + "Array");
+ for (int i = 0; i < entries; i++)
+ {
+ settings.setArrayIndex(i);
+ QVariant v = settings.value(arrayName, deflt);
+ array[i] = v.value<T2>();
+ }
+ settings.endArray();
+ }
+
+ template<typename T>
+ void writeSettings(QSettings& settings, const T& array, const QString& arrayName) const
+ {
+ settings.beginWriteArray(arrayName + "Array");
+ for (int i = 0; i < array.size(); i++)
+ {
+ settings.setArrayIndex(i);
+ settings.setValue(arrayName, array[i]);
+ }
+ settings.endArray();
+ }
+
+
+ public:
+ static const char* modifierName(int idx) { return m_ModifierNames[idx]; }
+ static const char* fixName(int idx) { return m_FixNames[idx]; }
+ static const char* switchCornerName(int idx) { return m_SwitchCornerNames[idx]; }
+
+ private:
+ static const char* m_ModifierNames[];
+ static const char* m_FixNames[];
+ static const char* m_SwitchCornerNames[];
+};
+
+#endif
diff --git a/src/gui/src/CommandProcess.cpp b/src/gui/src/CommandProcess.cpp
new file mode 100644
index 0000000..c85e847
--- /dev/null
+++ b/src/gui/src/CommandProcess.cpp
@@ -0,0 +1,63 @@
+/*
+ * barrier -- 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 "CommandProcess.h"
+
+#include <QProcess>
+#include <stdexcept>
+
+CommandProcess::CommandProcess(QString cmd, QStringList arguments, QString input) :
+ m_Command(cmd),
+ m_Arguments(arguments),
+ m_Input(input)
+{
+}
+
+QString CommandProcess::run()
+{
+ QProcess process;
+ process.setReadChannel(QProcess::StandardOutput);
+ process.start(m_Command, m_Arguments);
+ bool success = process.waitForStarted();
+
+ QString output, error;
+ if (success)
+ {
+ if (!m_Input.isEmpty()) {
+ process.write(m_Input.toStdString().c_str());
+ }
+
+ if (process.waitForFinished()) {
+ output = process.readAllStandardOutput().trimmed();
+ error = process.readAllStandardError().trimmed();
+ }
+ }
+
+ int code = process.exitCode();
+ if (!error.isEmpty() || !success || code != 0)
+ {
+ throw std::runtime_error(
+ QString("Code: %1\nError: %2")
+ .arg(process.exitCode())
+ .arg(error.isEmpty() ? "Unknown" : error)
+ .toStdString());
+ }
+
+ emit finished();
+
+ return output;
+}
diff --git a/src/gui/src/CommandProcess.h b/src/gui/src/CommandProcess.h
new file mode 100644
index 0000000..3e9763b
--- /dev/null
+++ b/src/gui/src/CommandProcess.h
@@ -0,0 +1,43 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#ifndef COMMANDTHREAD_H
+#define COMMANDTHREAD_H
+
+#include <QObject>
+#include <QStringList>
+
+class CommandProcess : public QObject
+{
+ Q_OBJECT
+
+public:
+ CommandProcess(QString cmd, QStringList arguments, QString input = "");
+
+signals:
+ void finished();
+
+public slots:
+ QString run();
+
+private:
+ QString m_Command;
+ QStringList m_Arguments;
+ QString m_Input;
+};
+
+#endif // COMMANDTHREAD_H
diff --git a/src/gui/src/CoreInterface.cpp b/src/gui/src/CoreInterface.cpp
new file mode 100644
index 0000000..d5ed40d
--- /dev/null
+++ b/src/gui/src/CoreInterface.cpp
@@ -0,0 +1,96 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "CoreInterface.h"
+
+#include "CommandProcess.h"
+#include "QUtility.h"
+
+#include <QCoreApplication>
+#include <QProcess>
+#include <QtGlobal>
+#include <QDir>
+#include <stdexcept>
+
+static const char kCoreBinary[] = "syntool";
+
+#ifdef Q_WS_WIN
+static const char kSerialKeyFilename[] = "Barrier.subkey";
+#else
+static const char kSerialKeyFilename[] = ".barrier.subkey";
+#endif
+
+CoreInterface::CoreInterface()
+{
+}
+
+QString CoreInterface::getProfileDir()
+{
+ QStringList args("--get-profile-dir");
+ return run(args);
+}
+
+QString CoreInterface::getInstalledDir()
+{
+ QStringList args("--get-installed-dir");
+ return run(args);
+}
+
+QString CoreInterface::getArch()
+{
+ QStringList args("--get-arch");
+ return run(args);
+}
+
+QString CoreInterface::getSerialKeyFilePath()
+{
+ QString filename = getProfileDir() + QDir::separator() + kSerialKeyFilename;
+ return filename;
+}
+
+QString CoreInterface::notifyUpdate (QString const& fromVersion,
+ QString const& toVersion,
+ QString const& serialKey) {
+ QStringList args("--notify-update");
+ QString input(fromVersion + ":" + toVersion + ":" + serialKey);
+ input.append("\n");
+ return run(args, input);
+}
+
+QString CoreInterface::notifyActivation(const QString& identity)
+{
+ QStringList args("--notify-activation");
+
+ QString input(identity + ":" + hash(getFirstMacAddress()));
+ QString os= getOSInformation();
+ if (!os.isEmpty()) {
+ input.append(":").append(os);
+ }
+ input.append("\n");
+
+ return run(args, input);
+}
+
+QString CoreInterface::run(const QStringList& args, const QString& input)
+{
+ QString program(
+ QCoreApplication::applicationDirPath()
+ + "/" + kCoreBinary);
+
+ CommandProcess commandProcess(program, args, input);
+ return commandProcess.run();
+}
diff --git a/src/gui/src/CoreInterface.h b/src/gui/src/CoreInterface.h
new file mode 100644
index 0000000..26b9c0a
--- /dev/null
+++ b/src/gui/src/CoreInterface.h
@@ -0,0 +1,36 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+#include <QString>
+
+class CoreInterface
+{
+public:
+ CoreInterface();
+
+ QString getProfileDir();
+ QString getInstalledDir();
+ QString getArch();
+ QString getSerialKeyFilePath();
+ QString notifyActivation(const QString& identity);
+ QString notifyUpdate (QString const& fromVersion,
+ QString const& toVersion,
+ QString const& serialKey);
+ QString run(const QStringList& args, const QString& input = "");
+};
diff --git a/src/gui/src/DataDownloader.cpp b/src/gui/src/DataDownloader.cpp
new file mode 100644
index 0000000..c71c73f
--- /dev/null
+++ b/src/gui/src/DataDownloader.cpp
@@ -0,0 +1,58 @@
+/*
+ * barrier -- 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 "DataDownloader.h"
+
+DataDownloader::DataDownloader(QObject* parent) :
+ QObject(parent),
+ m_pReply(nullptr),
+ m_IsFinished(false)
+{
+ connect(&m_NetworkManager, SIGNAL(finished(QNetworkReply*)),
+ SLOT(complete(QNetworkReply*)));
+}
+
+DataDownloader::~DataDownloader()
+{
+}
+
+void DataDownloader::complete(QNetworkReply* reply)
+{
+ m_Data = reply->readAll();
+ reply->deleteLater();
+
+ if (!m_Data.isEmpty()) {
+ m_IsFinished = true;
+ emit isComplete();
+ }
+}
+
+QByteArray DataDownloader::data() const
+{
+ return m_Data;
+}
+
+void DataDownloader::cancel()
+{
+ m_pReply->abort();
+}
+
+void DataDownloader::download(QUrl url)
+{
+ QNetworkRequest request(url);
+ m_pReply = m_NetworkManager.get(request);
+}
diff --git a/src/gui/src/DataDownloader.h b/src/gui/src/DataDownloader.h
new file mode 100644
index 0000000..46f7a0d
--- /dev/null
+++ b/src/gui/src/DataDownloader.h
@@ -0,0 +1,53 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#ifndef DATADOWNLOADER_H
+#define DATADOWNLOADER_H
+
+#include <QObject>
+#include <QByteArray>
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+
+class DataDownloader : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit DataDownloader(QObject* parent = 0);
+ virtual ~DataDownloader();
+
+ QByteArray data() const;
+ void cancel();
+ void download(QUrl url);
+ bool isFinished() const { return m_IsFinished; }
+
+signals:
+ void isComplete();
+
+private slots:
+ void complete(QNetworkReply* reply);
+
+private:
+ QNetworkAccessManager m_NetworkManager;
+ QByteArray m_Data;
+ QNetworkReply* m_pReply;
+ bool m_IsFinished;
+};
+
+#endif // DATADOWNLOADER_H
diff --git a/src/gui/src/DisplayIsValid.cpp b/src/gui/src/DisplayIsValid.cpp
new file mode 100644
index 0000000..5685c42
--- /dev/null
+++ b/src/gui/src/DisplayIsValid.cpp
@@ -0,0 +1,14 @@
+#ifdef WINAPI_XWINDOWS
+
+#include "DisplayIsValid.h"
+#include <X11/Xlib.h>
+
+bool display_is_valid()
+{
+ auto dsp = XOpenDisplay(NULL);
+ if (dsp != NULL)
+ XCloseDisplay(dsp);
+ return dsp != NULL;
+}
+
+#endif
diff --git a/src/gui/src/DisplayIsValid.h b/src/gui/src/DisplayIsValid.h
new file mode 100644
index 0000000..d5fcac8
--- /dev/null
+++ b/src/gui/src/DisplayIsValid.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#ifdef WINAPI_XWINDOWS
+bool display_is_valid();
+#endif
diff --git a/src/gui/src/ElevateMode.h b/src/gui/src/ElevateMode.h
new file mode 100644
index 0000000..b1dce7c
--- /dev/null
+++ b/src/gui/src/ElevateMode.h
@@ -0,0 +1,41 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2016 Symless
+ *
+ * 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
+
+// The elevate mode tristate determines two behaviours on Windows.
+// The first, switch-on-desk-switch (SodS), passed through barrierd as a
+// command line argument to barrier core, determines if the server restarts
+// when switching Windows desktops (e.g. when Windows UAC dialog pops up).
+// The second, passed as a boolean flag to Barrierd over the IPC inside
+// kIpcCommandMessage, determines whether Barrier should be started with
+// elevated privileges.
+//
+// The matrix for these two behaviours is as follows:
+// SodS Elevate
+// ___________________________
+// ElevateAsNeeded | true | false
+// ElevateAlways | false | true
+// ElevateNever | false | false
+//
+enum ElevateMode {
+ ElevateAsNeeded = 0,
+ ElevateAlways = 1,
+ ElevateNever = 2
+};
+
+extern const ElevateMode defaultElevateMode;
diff --git a/src/gui/src/Fingerprint.cpp b/src/gui/src/Fingerprint.cpp
new file mode 100644
index 0000000..be8b401
--- /dev/null
+++ b/src/gui/src/Fingerprint.cpp
@@ -0,0 +1,149 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "Fingerprint.h"
+
+#include "CoreInterface.h"
+
+#include <QDir>
+#include <QTextStream>
+
+static const char kDirName[] = "SSL/Fingerprints";
+static const char kLocalFilename[] = "Local.txt";
+static const char kTrustedServersFilename[] = "TrustedServers.txt";
+static const char kTrustedClientsFilename[] = "TrustedClients.txt";
+
+Fingerprint::Fingerprint(const QString& filename)
+{
+ m_Filename = filename;
+}
+
+void Fingerprint::trust(const QString& fingerprintText, bool append)
+{
+ Fingerprint::persistDirectory();
+
+ QIODevice::OpenMode openMode;
+ if (append) {
+ openMode = QIODevice::Append;
+ }
+ else {
+ openMode = QIODevice::WriteOnly;
+ }
+
+ QFile file(filePath());
+ if (file.open(openMode))
+ {
+ QTextStream out(&file);
+ out << fingerprintText << "\n";
+ file.close();
+ }
+}
+
+bool Fingerprint::fileExists() const
+{
+ QString dirName = Fingerprint::directoryPath();
+ if (!QDir(dirName).exists()) {
+ return false;
+ }
+
+ QFile file(filePath());
+ return file.exists();
+}
+
+bool Fingerprint::isTrusted(const QString& fingerprintText)
+{
+ QStringList list = readList();
+ foreach (QString trusted, list)
+ {
+ if (trusted == fingerprintText) {
+ return true;
+ }
+ }
+ return false;
+}
+
+QStringList Fingerprint::readList(const int readTo)
+{
+ QStringList list;
+
+ QString dirName = Fingerprint::directoryPath();
+ if (!QDir(dirName).exists()) {
+ return list;
+ }
+
+ QFile file(filePath());
+
+ if (file.open(QIODevice::ReadOnly))
+ {
+ QTextStream in(&file);
+ while (!in.atEnd())
+ {
+ list.append(in.readLine());
+ if (list.size() == readTo) {
+ break;
+ }
+ }
+ file.close();
+ }
+
+ return list;
+}
+
+QString Fingerprint::readFirst()
+{
+ QStringList list = readList(1);
+ return list.at(0);
+}
+
+QString Fingerprint::filePath() const
+{
+ QString dir = Fingerprint::directoryPath();
+ return QString("%1/%2").arg(dir).arg(m_Filename);
+}
+
+void Fingerprint::persistDirectory()
+{
+ QDir dir(Fingerprint::directoryPath());
+ if (!dir.exists()) {
+ dir.mkpath(".");
+ }
+}
+
+QString Fingerprint::directoryPath()
+{
+ CoreInterface coreInterface;
+ QString profileDir = coreInterface.getProfileDir();
+
+ return QString("%1/%2")
+ .arg(profileDir)
+ .arg(kDirName);
+}
+
+Fingerprint Fingerprint::local()
+{
+ return Fingerprint(kLocalFilename);
+}
+
+Fingerprint Fingerprint::trustedServers()
+{
+ return Fingerprint(kTrustedServersFilename);
+}
+
+Fingerprint Fingerprint::trustedClients()
+{
+ return Fingerprint(kTrustedClientsFilename);
+}
diff --git a/src/gui/src/Fingerprint.h b/src/gui/src/Fingerprint.h
new file mode 100644
index 0000000..ad5ce59
--- /dev/null
+++ b/src/gui/src/Fingerprint.h
@@ -0,0 +1,46 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+#include <QString>
+
+class Fingerprint
+{
+private:
+ Fingerprint(const QString& filename);
+
+public:
+ void trust(const QString& fingerprintText, bool append = true);
+ bool isTrusted(const QString& fingerprintText);
+ QStringList readList(const int readTo = -1);
+ QString readFirst();
+ QString filePath() const;
+ bool fileExists() const;
+
+public:
+ static Fingerprint local();
+ static Fingerprint trustedServers();
+ static Fingerprint trustedClients();
+ static QString directoryPath();
+ static QString localFingerprint();
+ static bool localFingerprintExists();
+ static void persistDirectory();
+
+private:
+ QString m_Filename;
+};
diff --git a/src/gui/src/Hotkey.cpp b/src/gui/src/Hotkey.cpp
new file mode 100644
index 0000000..c7138e7
--- /dev/null
+++ b/src/gui/src/Hotkey.cpp
@@ -0,0 +1,75 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "Hotkey.h"
+
+#include <QSettings>
+
+Hotkey::Hotkey() :
+ m_KeySequence(),
+ m_Actions()
+{
+}
+
+QString Hotkey::text() const
+{
+ QString text = keySequence().toString();
+
+ if (keySequence().isMouseButton())
+ return "mousebutton(" + text + ")";
+
+ return "keystroke(" + text + ")";
+}
+
+void Hotkey::loadSettings(QSettings& settings)
+{
+ keySequence().loadSettings(settings);
+
+ actions().clear();
+ int num = settings.beginReadArray("actions");
+ for (int i = 0; i < num; i++)
+ {
+ settings.setArrayIndex(i);
+ Action a;
+ a.loadSettings(settings);
+ actions().append(a);
+ }
+
+ settings.endArray();
+}
+
+void Hotkey::saveSettings(QSettings& settings) const
+{
+ keySequence().saveSettings(settings);
+
+ settings.beginWriteArray("actions");
+ for (int i = 0; i < actions().size(); i++)
+ {
+ settings.setArrayIndex(i);
+ actions()[i].saveSettings(settings);
+ }
+ settings.endArray();
+}
+
+QTextStream& operator<<(QTextStream& outStream, const Hotkey& hotkey)
+{
+ for (int i = 0; i < hotkey.actions().size(); i++)
+ outStream << "\t" << hotkey.text() << " = " << hotkey.actions()[i] << endl;
+
+ return outStream;
+}
diff --git a/src/gui/src/Hotkey.h b/src/gui/src/Hotkey.h
new file mode 100644
index 0000000..475da02
--- /dev/null
+++ b/src/gui/src/Hotkey.h
@@ -0,0 +1,66 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(HOTKEY_H)
+
+#define HOTKEY_H
+
+#include <QString>
+#include <QList>
+#include <QTextStream>
+
+#include "Action.h"
+#include "KeySequence.h"
+
+class HotkeyDialog;
+class ServerConfigDialog;
+class QSettings;
+
+class Hotkey
+{
+ friend class HotkeyDialog;
+ friend class ServerConfigDialog;
+ friend QTextStream& operator<<(QTextStream& outStream, const Hotkey& hotkey);
+
+ public:
+ Hotkey();
+
+ public:
+ QString text() const;
+ const KeySequence& keySequence() const { return m_KeySequence; }
+ const ActionList& actions() const { return m_Actions; }
+
+ void loadSettings(QSettings& settings);
+ void saveSettings(QSettings& settings) const;
+
+ protected:
+ KeySequence& keySequence() { return m_KeySequence; }
+ void setKeySequence(const KeySequence& seq) { m_KeySequence = seq; }
+ ActionList& actions() { return m_Actions; }
+
+
+ private:
+ KeySequence m_KeySequence;
+ ActionList m_Actions;
+};
+
+typedef QList<Hotkey> HotkeyList;
+
+QTextStream& operator<<(QTextStream& outStream, const Hotkey& hotkey);
+
+#endif
diff --git a/src/gui/src/HotkeyDialog.cpp b/src/gui/src/HotkeyDialog.cpp
new file mode 100644
index 0000000..ef25c3f
--- /dev/null
+++ b/src/gui/src/HotkeyDialog.cpp
@@ -0,0 +1,41 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "HotkeyDialog.h"
+
+#include <QtCore>
+#include <QtGui>
+
+HotkeyDialog::HotkeyDialog (QWidget* parent, Hotkey& hotkey) :
+ QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
+ Ui::HotkeyDialogBase(),
+ m_Hotkey(hotkey)
+{
+ setupUi(this);
+
+ m_pKeySequenceWidgetHotkey->setText(m_Hotkey.text());
+}
+
+void HotkeyDialog::accept()
+{
+ if (!sequenceWidget()->valid())
+ return;
+
+ hotkey().setKeySequence(sequenceWidget()->keySequence());
+ QDialog::accept();
+}
diff --git a/src/gui/src/HotkeyDialog.h b/src/gui/src/HotkeyDialog.h
new file mode 100644
index 0000000..a13fc24
--- /dev/null
+++ b/src/gui/src/HotkeyDialog.h
@@ -0,0 +1,49 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(HOTKEYDIALOG_H)
+
+#define HOTKEYDIALOG_H
+
+#include "ui_HotkeyDialogBase.h"
+#include "Hotkey.h"
+
+#include <QDialog>
+
+class HotkeyDialog : public QDialog, public Ui::HotkeyDialogBase
+{
+ Q_OBJECT
+
+ public:
+ HotkeyDialog(QWidget* parent, Hotkey& hotkey);
+
+ public:
+ const Hotkey& hotkey() const { return m_Hotkey; }
+
+ protected slots:
+ void accept();
+
+ protected:
+ const KeySequenceWidget* sequenceWidget() const { return m_pKeySequenceWidgetHotkey; }
+ Hotkey& hotkey() { return m_Hotkey; }
+
+ private:
+ Hotkey& m_Hotkey;
+};
+
+#endif
diff --git a/src/gui/src/HotkeyDialogBase.ui b/src/gui/src/HotkeyDialogBase.ui
new file mode 100644
index 0000000..0ebcb43
--- /dev/null
+++ b/src/gui/src/HotkeyDialogBase.ui
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HotkeyDialogBase</class>
+ <widget class="QDialog" name="HotkeyDialogBase">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>344</width>
+ <height>86</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Hotkey</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Enter the specification for the hotkey:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="KeySequenceWidget" name="m_pKeySequenceWidgetHotkey"/>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>KeySequenceWidget</class>
+ <extends>QLineEdit</extends>
+ <header>KeySequenceWidget.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>HotkeyDialogBase</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>HotkeyDialogBase</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/src/Ipc.cpp b/src/gui/src/Ipc.cpp
new file mode 100644
index 0000000..ed429ac
--- /dev/null
+++ b/src/gui/src/Ipc.cpp
@@ -0,0 +1,26 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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/>.
+ */
+
+// this class is a duplicate of /src/lib/ipc/Ipc.cpp
+
+#include "Ipc.h"
+
+const char* kIpcMsgHello = "IHEL%1i";
+const char* kIpcMsgLogLine = "ILOG%s";
+const char* kIpcMsgCommand = "ICMD%s%1i";
+const char* kIpcMsgShutdown = "ISDN";
diff --git a/src/gui/src/Ipc.h b/src/gui/src/Ipc.h
new file mode 100644
index 0000000..fe1ad08
--- /dev/null
+++ b/src/gui/src/Ipc.h
@@ -0,0 +1,42 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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/>.
+ */
+
+// this class is a duplicate of /src/lib/ipc/Ipc.h
+
+#pragma once
+
+#define IPC_HOST "127.0.0.1"
+#define IPC_PORT 24801
+
+enum qIpcMessageType {
+ kIpcHello,
+ kIpcLogLine,
+ kIpcCommand,
+ kIpcShutdown,
+};
+
+enum qIpcClientType {
+ kIpcClientUnknown,
+ kIpcClientGui,
+ kIpcClientNode,
+};
+
+extern const char* kIpcMsgHello;
+extern const char* kIpcMsgLogLine;
+extern const char* kIpcMsgCommand;
+extern const char* kIpcMsgShutdown;
diff --git a/src/gui/src/IpcClient.cpp b/src/gui/src/IpcClient.cpp
new file mode 100644
index 0000000..1b0e147
--- /dev/null
+++ b/src/gui/src/IpcClient.cpp
@@ -0,0 +1,146 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "IpcClient.h"
+#include <QTcpSocket>
+#include <QHostAddress>
+#include <iostream>
+#include <QTimer>
+#include "IpcReader.h"
+#include "Ipc.h"
+#include <QDataStream>
+
+IpcClient::IpcClient() :
+m_ReaderStarted(false),
+m_Enabled(false)
+{
+ m_Socket = new QTcpSocket(this);
+ connect(m_Socket, SIGNAL(connected()), this, SLOT(connected()));
+ connect(m_Socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(error(QAbstractSocket::SocketError)));
+
+ m_Reader = new IpcReader(m_Socket);
+ connect(m_Reader, SIGNAL(readLogLine(const QString&)), this, SLOT(handleReadLogLine(const QString&)));
+}
+
+IpcClient::~IpcClient()
+{
+}
+
+void IpcClient::connected()
+{
+ sendHello();
+ infoMessage("connection established");
+}
+
+void IpcClient::connectToHost()
+{
+ m_Enabled = true;
+
+ infoMessage("connecting to service...");
+ m_Socket->connectToHost(QHostAddress(QHostAddress::LocalHost), IPC_PORT);
+
+ if (!m_ReaderStarted) {
+ m_Reader->start();
+ m_ReaderStarted = true;
+ }
+}
+
+void IpcClient::disconnectFromHost()
+{
+ infoMessage("service disconnect");
+ m_Reader->stop();
+ m_Socket->close();
+}
+
+void IpcClient::error(QAbstractSocket::SocketError error)
+{
+ QString text;
+ switch (error) {
+ case 0: text = "connection refused"; break;
+ case 1: text = "remote host closed"; break;
+ default: text = QString("code=%1").arg(error); break;
+ }
+
+ errorMessage(QString("ipc connection error, %1").arg(text));
+
+ QTimer::singleShot(1000, this, SLOT(retryConnect()));
+}
+
+void IpcClient::retryConnect()
+{
+ if (m_Enabled) {
+ connectToHost();
+ }
+}
+
+void IpcClient::sendHello()
+{
+ QDataStream stream(m_Socket);
+ stream.writeRawData(kIpcMsgHello, 4);
+
+ char typeBuf[1];
+ typeBuf[0] = kIpcClientGui;
+ stream.writeRawData(typeBuf, 1);
+}
+
+void IpcClient::sendCommand(const QString& command, ElevateMode const elevate)
+{
+ QDataStream stream(m_Socket);
+
+ stream.writeRawData(kIpcMsgCommand, 4);
+
+ std::string stdStringCommand = command.toStdString();
+ const char* charCommand = stdStringCommand.c_str();
+ int length = (int)strlen(charCommand);
+
+ char lenBuf[4];
+ intToBytes(length, lenBuf, 4);
+ stream.writeRawData(lenBuf, 4);
+ stream.writeRawData(charCommand, length);
+
+ char elevateBuf[1];
+ // Refer to enum ElevateMode documentation for why this flag is mapped this way
+ elevateBuf[0] = (elevate == ElevateAlways) ? 1 : 0;
+ stream.writeRawData(elevateBuf, 1);
+}
+
+void IpcClient::handleReadLogLine(const QString& text)
+{
+ readLogLine(text);
+}
+
+// TODO: qt must have a built in way of converting int to bytes.
+void IpcClient::intToBytes(int value, char *buffer, int size)
+{
+ if (size == 1) {
+ buffer[0] = value & 0xff;
+ }
+ else if (size == 2) {
+ buffer[0] = (value >> 8) & 0xff;
+ buffer[1] = value & 0xff;
+ }
+ else if (size == 4) {
+ buffer[0] = (value >> 24) & 0xff;
+ buffer[1] = (value >> 16) & 0xff;
+ buffer[2] = (value >> 8) & 0xff;
+ buffer[3] = value & 0xff;
+ }
+ else {
+ // TODO: other sizes, if needed.
+ }
+}
diff --git a/src/gui/src/IpcClient.h b/src/gui/src/IpcClient.h
new file mode 100644
index 0000000..cd398b3
--- /dev/null
+++ b/src/gui/src/IpcClient.h
@@ -0,0 +1,63 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 <QObject>
+#include <QAbstractSocket>
+
+#include "ElevateMode.h"
+
+class QTcpSocket;
+class IpcReader;
+
+class IpcClient : public QObject
+{
+ Q_OBJECT
+
+public:
+ IpcClient();
+ virtual ~IpcClient();
+
+ void sendHello();
+ void sendCommand(const QString& command, ElevateMode elevate);
+ void connectToHost();
+ void disconnectFromHost();
+
+public slots:
+ void retryConnect();
+
+private:
+ void intToBytes(int value, char* buffer, int size);
+
+private slots:
+ void connected();
+ void error(QAbstractSocket::SocketError error);
+ void handleReadLogLine(const QString& text);
+
+signals:
+ void readLogLine(const QString& text);
+ void infoMessage(const QString& text);
+ void errorMessage(const QString& text);
+
+private:
+ QTcpSocket* m_Socket;
+ IpcReader* m_Reader;
+ bool m_ReaderStarted;
+ bool m_Enabled;
+};
diff --git a/src/gui/src/IpcReader.cpp b/src/gui/src/IpcReader.cpp
new file mode 100644
index 0000000..234f8aa
--- /dev/null
+++ b/src/gui/src/IpcReader.cpp
@@ -0,0 +1,140 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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/>.
+ */
+
+// uncomment to debug this end of IPC chatter
+//#define BARRIER_IPC_VERBOSE
+
+#include "IpcReader.h"
+#include <QTcpSocket>
+#include "Ipc.h"
+#include <QMutex>
+#include <QByteArray>
+
+#ifdef BARRIER_IPC_VERBOSE
+#include <iostream>
+#define IPC_LOG(x) (x)
+#else // not defined BARRIER_IPC_VERBOSE
+#define IPC_LOG(x)
+#endif
+
+IpcReader::IpcReader(QTcpSocket* socket) :
+m_Socket(socket)
+{
+}
+
+IpcReader::~IpcReader()
+{
+}
+
+void IpcReader::start()
+{
+ connect(m_Socket, SIGNAL(readyRead()), this, SLOT(read()));
+}
+
+void IpcReader::stop()
+{
+ disconnect(m_Socket, SIGNAL(readyRead()), this, SLOT(read()));
+}
+
+void IpcReader::read()
+{
+ QMutexLocker locker(&m_Mutex);
+ IPC_LOG(std::cout << "ready read" << std::endl);
+
+ while (m_Socket->bytesAvailable()) {
+ IPC_LOG(std::cout << "bytes available" << std::endl);
+
+ char codeBuf[5];
+ readStream(codeBuf, 4);
+ codeBuf[4] = 0;
+ IPC_LOG(std::cout << "ipc read: " << codeBuf << std::endl);
+
+ if (memcmp(codeBuf, kIpcMsgLogLine, 4) == 0) {
+ IPC_LOG(std::cout << "reading log line" << std::endl);
+
+ char lenBuf[4];
+ readStream(lenBuf, 4);
+ int len = bytesToInt(lenBuf, 4);
+
+ char* data = new char[len];
+ readStream(data, len);
+ QString line = QString::fromUtf8(data, len);
+ delete[] data;
+
+ readLogLine(line);
+ }
+ else {
+ IPC_LOG(std::cerr << "aborting, message invalid" << std::endl);
+ return;
+ }
+ }
+
+ IPC_LOG(std::cout << "read done" << std::endl);
+}
+
+bool IpcReader::readStream(char* buffer, int length)
+{
+ IPC_LOG(std::cout << "reading stream" << std::endl);
+
+ int read = 0;
+ while (read < length) {
+ int ask = length - read;
+ if (m_Socket->bytesAvailable() < ask) {
+ IPC_LOG(std::cout << "buffer too short, waiting" << std::endl);
+ m_Socket->waitForReadyRead(-1);
+ }
+
+ int got = m_Socket->read(buffer, ask);
+ read += got;
+
+ IPC_LOG(std::cout << "> ask=" << ask << " got=" << got
+ << " read=" << read << std::endl);
+
+ if (got == -1) {
+ IPC_LOG(std::cout << "socket ended, aborting" << std::endl);
+ return false;
+ }
+ else if (length - read > 0) {
+ IPC_LOG(std::cout << "more remains, seek to " << got << std::endl);
+ buffer += got;
+ }
+ }
+ return true;
+}
+
+int IpcReader::bytesToInt(const char *buffer, int size)
+{
+ if (size == 1) {
+ return (unsigned char)buffer[0];
+ }
+ else if (size == 2) {
+ return
+ (((unsigned char)buffer[0]) << 8) +
+ (unsigned char)buffer[1];
+ }
+ else if (size == 4) {
+ return
+ (((unsigned char)buffer[0]) << 24) +
+ (((unsigned char)buffer[1]) << 16) +
+ (((unsigned char)buffer[2]) << 8) +
+ (unsigned char)buffer[3];
+ }
+ else {
+ return 0;
+ }
+}
diff --git a/src/gui/src/IpcReader.h b/src/gui/src/IpcReader.h
new file mode 100644
index 0000000..388d913
--- /dev/null
+++ b/src/gui/src/IpcReader.h
@@ -0,0 +1,49 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 <QObject>
+#include <QMutex>
+
+class QTcpSocket;
+
+class IpcReader : public QObject
+{
+ Q_OBJECT;
+
+public:
+ IpcReader(QTcpSocket* socket);
+ virtual ~IpcReader();
+ void start();
+ void stop();
+
+signals:
+ void readLogLine(const QString& text);
+
+private:
+ bool readStream(char* buffer, int length);
+ int bytesToInt(const char* buffer, int size);
+
+private slots:
+ void read();
+
+private:
+ QTcpSocket* m_Socket;
+ QMutex m_Mutex;
+};
diff --git a/src/gui/src/KeySequence.cpp b/src/gui/src/KeySequence.cpp
new file mode 100644
index 0000000..cc74cb2
--- /dev/null
+++ b/src/gui/src/KeySequence.cpp
@@ -0,0 +1,237 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "KeySequence.h"
+
+#include <QtCore>
+#include <QtGui>
+
+// this table originally comes from Qt sources (gui/kernel/qkeysequence.cpp)
+// and is heavily modified
+static const struct
+{
+ int key;
+ const char* name;
+} keyname[] =
+{
+ { Qt::Key_Space, "Space" },
+ { Qt::Key_Escape, "Escape" },
+ { Qt::Key_Tab, "Tab" },
+ { Qt::Key_Backtab, "LeftTab" },
+ { Qt::Key_Backspace, "BackSpace" },
+ { Qt::Key_Return, "Return" },
+ { Qt::Key_Insert, "Insert" },
+ { Qt::Key_Delete, "Delete" },
+ { Qt::Key_Pause, "Pause" },
+ { Qt::Key_Print, "Print" },
+ { Qt::Key_SysReq, "SysReq" },
+ { Qt::Key_Home, "Home" },
+ { Qt::Key_End, "End" },
+ { Qt::Key_Left, "Left" },
+ { Qt::Key_Up, "Up" },
+ { Qt::Key_Right, "Right" },
+ { Qt::Key_Down, "Down" },
+ { Qt::Key_PageUp, "PageUp" },
+ { Qt::Key_PageDown, "PageDown" },
+ { Qt::Key_CapsLock, "CapsLock" },
+ { Qt::Key_NumLock, "NumLock" },
+ { Qt::Key_ScrollLock, "ScrollLock" },
+ { Qt::Key_Menu, "Menu" },
+ { Qt::Key_Help, "Help" },
+ { Qt::Key_Enter, "KP_Enter" },
+ { Qt::Key_Clear, "Clear" },
+
+ { Qt::Key_Back, "WWWBack" },
+ { Qt::Key_Forward, "WWWForward" },
+ { Qt::Key_Stop, "WWWStop" },
+ { Qt::Key_Refresh, "WWWRefresh" },
+ { Qt::Key_VolumeDown, "AudioDown" },
+ { Qt::Key_VolumeMute, "AudioMute" },
+ { Qt::Key_VolumeUp, "AudioUp" },
+ { Qt::Key_MediaPlay, "AudioPlay" },
+ { Qt::Key_MediaStop, "AudioStop" },
+ { Qt::Key_MediaPrevious,"AudioPrev" },
+ { Qt::Key_MediaNext, "AudioNext" },
+ { Qt::Key_HomePage, "WWWHome" },
+ { Qt::Key_Favorites, "WWWFavorites" },
+ { Qt::Key_Search, "WWWSearch" },
+ { Qt::Key_Standby, "Sleep" },
+ { Qt::Key_LaunchMail, "AppMail" },
+ { Qt::Key_LaunchMedia, "AppMedia" },
+ { Qt::Key_Launch0, "AppUser1" },
+ { Qt::Key_Launch1, "AppUser2" },
+ { Qt::Key_Select, "Select" },
+
+ { 0, 0 }
+};
+
+KeySequence::KeySequence() :
+ m_Sequence(),
+ m_Modifiers(0),
+ m_IsValid(false)
+{
+}
+
+bool KeySequence::isMouseButton() const
+{
+ return !m_Sequence.isEmpty() && m_Sequence.last() < Qt::Key_Space;
+}
+
+QString KeySequence::toString() const
+{
+ QString result;
+
+ for (int i = 0; i < m_Sequence.size(); i++)
+ {
+ result += keyToString(m_Sequence[i]);
+
+ if (i != m_Sequence.size() - 1)
+ result += "+";
+ }
+
+ return result;
+}
+
+bool KeySequence::appendMouseButton(int button)
+{
+ return appendKey(button, 0);
+}
+
+bool KeySequence::appendKey(int key, int modifiers)
+{
+ if (m_Sequence.size() == 4)
+ return true;
+
+ switch(key)
+ {
+ case Qt::Key_AltGr:
+ return false;
+
+ case Qt::Key_Control:
+ case Qt::Key_Alt:
+ case Qt::Key_Shift:
+ case Qt::Key_Meta:
+ case Qt::Key_Menu:
+ {
+ int mod = modifiers & (~m_Modifiers);
+ if (mod)
+ {
+ m_Sequence.append(mod);
+ m_Modifiers |= mod;
+ }
+ }
+ break;
+
+ default:
+ // see if we can handle this key, if not, don't accept it
+ if (keyToString(key).isEmpty())
+ break;
+
+ m_Sequence.append(key);
+ setValid(true);
+ return true;
+ }
+
+ return false;
+}
+
+void KeySequence::loadSettings(QSettings& settings)
+{
+ sequence().clear();
+ int num = settings.beginReadArray("keys");
+ for (int i = 0; i < num; i++)
+ {
+ settings.setArrayIndex(i);
+ sequence().append(settings.value("key", 0).toInt());
+ }
+ settings.endArray();
+
+ setModifiers(0);
+ setValid(true);
+}
+
+void KeySequence::saveSettings(QSettings& settings) const
+{
+ settings.beginWriteArray("keys");
+ for (int i = 0; i < sequence().size(); i++)
+ {
+ settings.setArrayIndex(i);
+ settings.setValue("key", sequence()[i]);
+ }
+ settings.endArray();
+}
+
+QString KeySequence::keyToString(int key)
+{
+ // nothing there?
+ if (key == 0)
+ return "";
+
+ // a hack to handle mouse buttons as if they were keys
+ if (key < Qt::Key_Space)
+ {
+ switch(key)
+ {
+ case Qt::LeftButton: return "1";
+ case Qt::RightButton: return "2";
+ case Qt::MidButton: return "3";
+ }
+
+ return "4"; // qt only knows three mouse buttons, so assume it's an unknown fourth one
+ }
+
+ // modifiers?
+ if (key & Qt::ShiftModifier)
+ return "Shift";
+
+ if (key & Qt::ControlModifier)
+ return "Control";
+
+ if (key & Qt::AltModifier)
+ return "Alt";
+
+ if (key & Qt::MetaModifier)
+ return "Meta";
+
+ // treat key pad like normal keys (FIXME: we should have another lookup table for keypad keys instead)
+ key &= ~Qt::KeypadModifier;
+
+ // a printable 7 bit character?
+ if (key < 0x80 && key != Qt::Key_Space)
+ return QChar(key & 0x7f).toLower();
+
+ // a function key?
+ if (key >= Qt::Key_F1 && key <= Qt::Key_F35)
+ return QString::fromUtf8("F%1").arg(key - Qt::Key_F1 + 1);
+
+ // a special key?
+ int i=0;
+ while (keyname[i].name)
+ {
+ if (key == keyname[i].key)
+ return QString::fromUtf8(keyname[i].name);
+ i++;
+ }
+
+ // representable in ucs2?
+ if (key < 0x10000)
+ return QString("\\u%1").arg(QChar(key).toLower().unicode(), 4, 16, QChar('0'));
+
+ // give up, barrier probably won't handle this
+ return "";
+}
diff --git a/src/gui/src/KeySequence.h b/src/gui/src/KeySequence.h
new file mode 100644
index 0000000..8d9706d
--- /dev/null
+++ b/src/gui/src/KeySequence.h
@@ -0,0 +1,58 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(KEYSEQUENCE_H)
+
+#define KEYSEQUENCE_H
+
+#include <QList>
+#include <QString>
+
+class QSettings;
+
+class KeySequence
+{
+ public:
+ KeySequence();
+
+ public:
+ QString toString() const;
+ bool appendKey(int modifiers, int key);
+ bool appendMouseButton(int button);
+ bool isMouseButton() const;
+ bool valid() const { return m_IsValid; }
+ int modifiers() const { return m_Modifiers; }
+ void saveSettings(QSettings& settings) const;
+ void loadSettings(QSettings& settings);
+ const QList<int>& sequence() const { return m_Sequence; }
+
+ private:
+ void setValid(bool b) { m_IsValid = b; }
+ void setModifiers(int i) { m_Modifiers = i; }
+ QList<int>& sequence() { return m_Sequence; }
+
+ private:
+ QList<int> m_Sequence;
+ int m_Modifiers;
+ bool m_IsValid;
+
+ static QString keyToString(int key);
+};
+
+#endif
+
diff --git a/src/gui/src/KeySequenceWidget.cpp b/src/gui/src/KeySequenceWidget.cpp
new file mode 100644
index 0000000..e5823e1
--- /dev/null
+++ b/src/gui/src/KeySequenceWidget.cpp
@@ -0,0 +1,145 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "KeySequenceWidget.h"
+
+#include <iostream>
+#include <QMouseEvent>
+
+KeySequenceWidget::KeySequenceWidget(QWidget* parent, const KeySequence& seq) :
+ QPushButton(parent),
+ m_KeySequence(seq),
+ m_BackupSequence(seq),
+ m_Status(Stopped),
+ m_MousePrefix("mousebutton("),
+ m_MousePostfix(")"),
+ m_KeyPrefix("keystroke("),
+ m_KeyPostfix(")")
+{
+ setFocusPolicy(Qt::NoFocus);
+ updateOutput();
+}
+
+void KeySequenceWidget::setKeySequence(const KeySequence& seq)
+{
+ keySequence() = seq;
+ backupSequence() = seq;
+
+ setStatus(Stopped);
+ updateOutput();
+}
+
+void KeySequenceWidget::mousePressEvent(QMouseEvent* event)
+{
+ event->accept();
+
+ if (status() == Stopped)
+ {
+ startRecording();
+ return;
+ }
+
+ if (m_KeySequence.appendMouseButton(event->button()))
+ stopRecording();
+
+ updateOutput();
+}
+
+void KeySequenceWidget::startRecording()
+{
+ keySequence() = KeySequence();
+ setDown(true);
+ setFocus();
+ grabKeyboard();
+ setStatus(Recording);
+}
+
+void KeySequenceWidget::stopRecording()
+{
+ if (!keySequence().valid())
+ {
+ keySequence() = backupSequence();
+ updateOutput();
+ }
+
+ setDown(false);
+ focusNextChild();
+ releaseKeyboard();
+ setStatus(Stopped);
+ emit keySequenceChanged();
+}
+
+bool KeySequenceWidget::event(QEvent* event)
+{
+ if (status() == Recording)
+ {
+ switch(event->type())
+ {
+ case QEvent::KeyPress:
+ keyPressEvent(static_cast<QKeyEvent*>(event));
+ return true;
+
+ case QEvent::MouseButtonRelease:
+ event->accept();
+ return true;
+
+ case QEvent::ShortcutOverride:
+ event->accept();
+ return true;
+
+ case QEvent::FocusOut:
+ stopRecording();
+ if (!valid())
+ {
+ keySequence() = backupSequence();
+ updateOutput();
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return QPushButton::event(event);
+}
+
+void KeySequenceWidget::keyPressEvent(QKeyEvent* event)
+{
+ event->accept();
+
+ if (status() == Stopped)
+ return;
+
+ if (m_KeySequence.appendKey(event->key(), event->modifiers()))
+ stopRecording();
+
+ updateOutput();
+}
+
+void KeySequenceWidget::updateOutput()
+{
+ QString s;
+
+ if (m_KeySequence.isMouseButton())
+ s = mousePrefix() + m_KeySequence.toString() + mousePostfix();
+ else
+ s = keyPrefix() + m_KeySequence.toString() + keyPostfix();
+
+ setText(s);
+}
diff --git a/src/gui/src/KeySequenceWidget.h b/src/gui/src/KeySequenceWidget.h
new file mode 100644
index 0000000..eaef514
--- /dev/null
+++ b/src/gui/src/KeySequenceWidget.h
@@ -0,0 +1,81 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(KEYSEQUENCEWIDGET__H)
+
+#define KEYSEQUENCEWIDGET__H
+
+#include <QPushButton>
+
+#include "KeySequence.h"
+
+class KeySequenceWidget : public QPushButton
+{
+ Q_OBJECT
+
+ public:
+ KeySequenceWidget(QWidget* parent, const KeySequence& seq = KeySequence());
+
+ signals:
+ void keySequenceChanged();
+
+ public:
+ const QString& mousePrefix() const { return m_MousePrefix; }
+ const QString& mousePostfix() const { return m_MousePostfix; }
+ const QString& keyPrefix() const { return m_KeyPrefix; }
+ const QString& keyPostfix() const { return m_KeyPostfix; }
+
+ void setMousePrefix(const QString& s) { m_MousePrefix = s; }
+ void setMousePostfix(const QString& s) { m_MousePostfix = s; }
+ void setKeyPrefix(const QString& s) { m_KeyPrefix = s; }
+ void setKeyPostfix(const QString& s) { m_KeyPostfix = s; }
+
+ const KeySequence& keySequence() const { return m_KeySequence; }
+ const KeySequence& backupSequence() const { return m_BackupSequence; }
+ void setKeySequence(const KeySequence& seq);
+
+ bool valid() const { return keySequence().valid(); }
+
+ protected:
+ void mousePressEvent(QMouseEvent*);
+ void keyPressEvent(QKeyEvent*);
+ bool event(QEvent* event);
+ void appendToSequence(int key);
+ void updateOutput();
+ void startRecording();
+ void stopRecording();
+ KeySequence& keySequence() { return m_KeySequence; }
+ KeySequence& backupSequence() { return m_BackupSequence; }
+
+ private:
+ enum Status { Stopped, Recording };
+ void setStatus(Status s) { m_Status = s; }
+ Status status() const { return m_Status; }
+
+ private:
+ KeySequence m_KeySequence;
+ KeySequence m_BackupSequence;
+ Status m_Status;
+ QString m_MousePrefix;
+ QString m_MousePostfix;
+ QString m_KeyPrefix;
+ QString m_KeyPostfix;
+};
+
+#endif
+
diff --git a/src/gui/src/LogWindow.cpp b/src/gui/src/LogWindow.cpp
new file mode 100644
index 0000000..6aee096
--- /dev/null
+++ b/src/gui/src/LogWindow.cpp
@@ -0,0 +1,72 @@
+/*
+* barrier -- mouse and keyboard sharing utility
+* Copyright (C) 2018 Debauchee Open Source Group
+*
+* 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 "LogWindow.h"
+
+#include <QDateTime>
+
+static QString getTimeStamp()
+{
+ QDateTime current = QDateTime::currentDateTime();
+ return '[' + current.toString(Qt::ISODate) + ']';
+}
+
+LogWindow::LogWindow(QWidget *parent) :
+ QDialog(parent)
+{
+ // explicitly unset DeleteOnClose so the log window can be show and hidden
+ // repeatedly until Barrier is finished
+ setAttribute(Qt::WA_DeleteOnClose, false);
+ setupUi(this);
+}
+
+void LogWindow::startNewInstance()
+{
+ // put a space between last log output and new instance.
+ if (!m_pLogOutput->toPlainText().isEmpty())
+ appendRaw("");
+}
+
+void LogWindow::appendInfo(const QString& text)
+{
+ appendRaw(getTimeStamp() + " INFO: " + text);
+}
+
+void LogWindow::appendDebug(const QString& text)
+{
+ appendRaw(getTimeStamp() + " DEBUG: " + text);
+}
+
+void LogWindow::appendError(const QString& text)
+{
+ appendRaw(getTimeStamp() + " ERROR: " + text);
+}
+
+void LogWindow::appendRaw(const QString& text)
+{
+ m_pLogOutput->append(text);
+}
+
+void LogWindow::on_m_pButtonHide_clicked()
+{
+ hide();
+}
+
+void LogWindow::on_m_pButtonClearLog_clicked()
+{
+ m_pLogOutput->clear();
+}
diff --git a/src/gui/src/LogWindow.h b/src/gui/src/LogWindow.h
new file mode 100644
index 0000000..af23030
--- /dev/null
+++ b/src/gui/src/LogWindow.h
@@ -0,0 +1,46 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2018 Debauchee Open Source Group
+ *
+ * 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/>.
+ */
+
+#if !defined(LOGWINDOW__H)
+
+#define LOGWINDOW__H
+
+#include <QDialog>
+
+#include "ui_LogWindowBase.h"
+
+class LogWindow : public QDialog, public Ui::LogWindowBase
+{
+ Q_OBJECT
+
+ public:
+ LogWindow(QWidget *parent);
+
+ void startNewInstance();
+
+ void appendRaw(const QString& text);
+ void appendInfo(const QString& text);
+ void appendDebug(const QString& text);
+ void appendError(const QString& text);
+
+ private slots:
+ void on_m_pButtonHide_clicked();
+ void on_m_pButtonClearLog_clicked();
+
+};
+
+#endif // LOGWINDOW__H
diff --git a/src/gui/src/LogWindowBase.ui b/src/gui/src/LogWindowBase.ui
new file mode 100644
index 0000000..f335947
--- /dev/null
+++ b/src/gui/src/LogWindowBase.ui
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LogWindowBase</class>
+ <widget class="QDialog" name="LogWindowBase">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>800</width>
+ <height>371</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>400</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Log - Barrier</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QTextEdit" name="m_pLogOutput">
+ <property name="font">
+ <font>
+ <family>Courier</family>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="lineWrapMode">
+ <enum>QTextEdit::NoWrap</enum>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <spacer name="spacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_pButtonClearLog">
+ <property name="text">
+ <string>&amp;Clear Log</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_pButtonHide">
+ <property name="text">
+ <string>&amp;Hide</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+</ui>
diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp
new file mode 100644
index 0000000..b92e5f7
--- /dev/null
+++ b/src/gui/src/MainWindow.cpp
@@ -0,0 +1,1279 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 <iostream>
+
+#include "MainWindow.h"
+
+#include "Fingerprint.h"
+#include "AboutDialog.h"
+#include "ServerConfigDialog.h"
+#include "SettingsDialog.h"
+#include "ZeroconfService.h"
+#include "DataDownloader.h"
+#include "CommandProcess.h"
+#include "QUtility.h"
+#include "ProcessorArch.h"
+#include "SslCertificate.h"
+#include "ShutdownCh.h"
+
+#include <QtCore>
+#include <QtGui>
+#include <QtNetwork>
+#include <QNetworkAccessManager>
+#include <QMenu>
+#include <QMenuBar>
+#include <QMessageBox>
+#include <QFileDialog>
+#include <QDesktopServices>
+#include <QDesktopWidget>
+
+#if defined(Q_OS_MAC)
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+#if defined(Q_OS_WIN)
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#endif
+
+#if defined(Q_OS_WIN)
+static const char barrierConfigName[] = "barrier.sgc";
+static const QString barrierConfigFilter(QObject::tr("Barrier Configurations (*.sgc);;All files (*.*)"));
+static QString bonjourBaseUrl = "http://binaries.symless.com/bonjour/";
+static const char bonjourFilename32[] = "Bonjour.msi";
+static const char bonjourFilename64[] = "Bonjour64.msi";
+static const char bonjourTargetFilename[] = "Bonjour.msi";
+#else
+static const char barrierConfigName[] = "barrier.conf";
+static const QString barrierConfigFilter(QObject::tr("Barrier Configurations (*.conf);;All files (*.*)"));
+#endif
+
+static const char* barrierIconFiles[] =
+{
+ ":/res/icons/16x16/barrier-disconnected.png",
+ ":/res/icons/16x16/barrier-disconnected.png",
+ ":/res/icons/16x16/barrier-connected.png",
+ ":/res/icons/16x16/barrier-transfering.png"
+};
+
+MainWindow::MainWindow(QSettings& settings, AppConfig& appConfig) :
+ m_Settings(settings),
+ m_AppConfig(&appConfig),
+ m_pBarrier(NULL),
+ m_BarrierState(barrierDisconnected),
+ m_ServerConfig(&m_Settings, 5, 3, m_AppConfig->screenName(), this),
+ m_pTempConfigFile(NULL),
+ m_pTrayIcon(NULL),
+ m_pTrayIconMenu(NULL),
+ m_AlreadyHidden(false),
+ m_pMenuBar(NULL),
+ m_pMenuBarrier(NULL),
+ m_pMenuHelp(NULL),
+ m_pZeroconfService(NULL),
+ m_pDataDownloader(NULL),
+ m_DownloadMessageBox(NULL),
+ m_pCancelButton(NULL),
+ m_SuppressAutoConfigWarning(false),
+ m_BonjourInstall(NULL),
+ m_SuppressEmptyServerWarning(false),
+ m_ExpectedRunningState(kStopped),
+ m_pSslCertificate(NULL),
+ m_pLogWindow(new LogWindow(nullptr))
+{
+ // explicitly unset DeleteOnClose so the window can be show and hidden
+ // repeatedly until Barrier is finished
+ setAttribute(Qt::WA_DeleteOnClose, false);
+ // mark the windows as sort of "dialog" window so that tiling window
+ // managers will float it by default (X11)
+ setAttribute(Qt::WA_X11NetWmWindowTypeDialog, true);
+
+ setupUi(this);
+
+ createMenuBar();
+ loadSettings();
+ initConnections();
+
+ m_pLabelScreenName->setText(getScreenName());
+ m_pLabelIpAddresses->setText(getIPAddresses());
+
+#if defined(Q_OS_WIN)
+ // ipc must always be enabled, so that we can disable command when switching to desktop mode.
+ connect(&m_IpcClient, SIGNAL(readLogLine(const QString&)), this, SLOT(appendLogRaw(const QString&)));
+ connect(&m_IpcClient, SIGNAL(errorMessage(const QString&)), this, SLOT(appendLogError(const QString&)));
+ connect(&m_IpcClient, SIGNAL(infoMessage(const QString&)), this, SLOT(appendLogInfo(const QString&)));
+ m_IpcClient.connectToHost();
+#endif
+
+ // change default size based on os
+#if defined(Q_OS_MAC)
+ resize(720, 550);
+ setMinimumSize(720, 0);
+#elif defined(Q_OS_LINUX)
+ resize(700, 530);
+ setMinimumSize(700, 0);
+#endif
+
+ m_SuppressAutoConfigWarning = true;
+ m_pCheckBoxAutoConfig->setChecked(appConfig.autoConfig());
+ m_SuppressAutoConfigWarning = false;
+
+ m_pComboServerList->hide();
+ m_pLabelPadlock->hide();
+
+ updateSSLFingerprint();
+
+ // resize window to smallest reasonable size
+ resize(0, 0);
+}
+
+MainWindow::~MainWindow()
+{
+ if (appConfig().processMode() == Desktop) {
+ m_ExpectedRunningState = kStopped;
+ stopDesktop();
+ }
+
+ saveSettings();
+
+ delete m_pZeroconfService;
+ delete m_DownloadMessageBox;
+ delete m_BonjourInstall;
+ delete m_pSslCertificate;
+
+ // LogWindow is created as a sibling of the MainWindow rather than a child
+ // so that the main window can be hidden without hiding the log. because of
+ // this it does not get properly cleaned up by the QObject system. also by
+ // the time this destructor is called the event loop will no longer be able
+ // to clean up the LogWindow so ->deleteLater() will not work
+ delete m_pLogWindow;
+}
+
+void MainWindow::open()
+{
+ createTrayIcon();
+
+ if (appConfig().getAutoHide()) {
+ hide();
+ } else {
+ showNormal();
+ }
+
+ if (!appConfig().autoConfigPrompted()) {
+ promptAutoConfig();
+ }
+
+ // only start if user has previously started. this stops the gui from
+ // auto hiding before the user has configured barrier (which of course
+ // confuses first time users, who think barrier has crashed).
+ if (appConfig().startedBefore() && appConfig().processMode() == Desktop) {
+ m_SuppressEmptyServerWarning = true;
+ startBarrier();
+ m_SuppressEmptyServerWarning = false;
+ }
+}
+
+void MainWindow::setStatus(const QString &status)
+{
+ m_pStatusLabel->setText(status);
+}
+
+void MainWindow::createTrayIcon()
+{
+ m_pTrayIconMenu = new QMenu(this);
+
+ m_pTrayIconMenu->addAction(m_pActionStartBarrier);
+ m_pTrayIconMenu->addAction(m_pActionStopBarrier);
+ m_pTrayIconMenu->addAction(m_pActionShowLog);
+ m_pTrayIconMenu->addSeparator();
+
+ m_pTrayIconMenu->addAction(m_pActionMinimize);
+ m_pTrayIconMenu->addAction(m_pActionRestore);
+ m_pTrayIconMenu->addSeparator();
+ m_pTrayIconMenu->addAction(m_pActionQuit);
+
+ m_pTrayIcon = new QSystemTrayIcon(this);
+ m_pTrayIcon->setContextMenu(m_pTrayIconMenu);
+ m_pTrayIcon->setToolTip("Barrier");
+
+ connect(m_pTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
+ this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
+
+ setIcon(barrierDisconnected);
+
+ m_pTrayIcon->show();
+}
+
+void MainWindow::retranslateMenuBar()
+{
+ m_pMenuBarrier->setTitle(tr("&Barrier"));
+ m_pMenuHelp->setTitle(tr("&Help"));
+}
+
+void MainWindow::createMenuBar()
+{
+ m_pMenuBar = new QMenuBar(this);
+ m_pMenuBarrier = new QMenu("", m_pMenuBar);
+ m_pMenuHelp = new QMenu("", m_pMenuBar);
+ retranslateMenuBar();
+
+ m_pMenuBar->addAction(m_pMenuBarrier->menuAction());
+ m_pMenuBar->addAction(m_pMenuHelp->menuAction());
+
+ m_pMenuBarrier->addAction(m_pActionShowLog);
+ m_pMenuBarrier->addAction(m_pActionSettings);
+ m_pMenuBarrier->addAction(m_pActionMinimize);
+ m_pMenuBarrier->addSeparator();
+ m_pMenuBarrier->addAction(m_pActionSave);
+ m_pMenuBarrier->addSeparator();
+ m_pMenuBarrier->addAction(m_pActionQuit);
+ m_pMenuHelp->addAction(m_pActionAbout);
+
+ setMenuBar(m_pMenuBar);
+}
+
+void MainWindow::loadSettings()
+{
+ // the next two must come BEFORE loading groupServerChecked and groupClientChecked or
+ // disabling and/or enabling the right widgets won't automatically work
+ m_pRadioExternalConfig->setChecked(settings().value("useExternalConfig", false).toBool());
+ m_pRadioInternalConfig->setChecked(settings().value("useInternalConfig", true).toBool());
+
+ m_pGroupServer->setChecked(settings().value("groupServerChecked", false).toBool());
+ m_pLineEditConfigFile->setText(settings().value("configFile", QDir::homePath() + "/" + barrierConfigName).toString());
+ m_pGroupClient->setChecked(settings().value("groupClientChecked", true).toBool());
+ m_pLineEditHostname->setText(settings().value("serverHostname").toString());
+}
+
+void MainWindow::initConnections()
+{
+ connect(m_pActionMinimize, SIGNAL(triggered()), this, SLOT(hide()));
+ connect(m_pActionRestore, SIGNAL(triggered()), this, SLOT(showNormal()));
+ connect(m_pActionStartBarrier, SIGNAL(triggered()), this, SLOT(startBarrier()));
+ connect(m_pActionStopBarrier, SIGNAL(triggered()), this, SLOT(stopBarrier()));
+ connect(m_pActionShowLog, SIGNAL(triggered()), this, SLOT(showLogWindow()));
+ connect(m_pActionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
+}
+
+void MainWindow::saveSettings()
+{
+ // program settings
+ settings().setValue("groupServerChecked", m_pGroupServer->isChecked());
+ settings().setValue("useExternalConfig", m_pRadioExternalConfig->isChecked());
+ settings().setValue("configFile", m_pLineEditConfigFile->text());
+ settings().setValue("useInternalConfig", m_pRadioInternalConfig->isChecked());
+ settings().setValue("groupClientChecked", m_pGroupClient->isChecked());
+ settings().setValue("serverHostname", m_pLineEditHostname->text());
+
+ settings().sync();
+}
+
+void MainWindow::setIcon(qBarrierState state)
+{
+ QIcon icon;
+ icon.addFile(barrierIconFiles[state]);
+
+ setWindowIcon(icon);
+
+ if (m_pTrayIcon)
+ m_pTrayIcon->setIcon(icon);
+}
+
+void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason)
+{
+ if (reason == QSystemTrayIcon::DoubleClick)
+ {
+ if (isVisible())
+ {
+ hide();
+ }
+ else
+ {
+ showNormal();
+ activateWindow();
+ }
+ }
+}
+
+void MainWindow::logOutput()
+{
+ if (m_pBarrier)
+ {
+ QString text(m_pBarrier->readAllStandardOutput());
+ foreach(QString line, text.split(QRegExp("\r|\n|\r\n")))
+ {
+ if (!line.isEmpty())
+ {
+ appendLogRaw(line);
+ }
+ }
+ }
+}
+
+void MainWindow::logError()
+{
+ if (m_pBarrier)
+ {
+ appendLogRaw(m_pBarrier->readAllStandardError());
+ }
+}
+
+void MainWindow::appendLogInfo(const QString& text)
+{
+ m_pLogWindow->appendInfo(text);
+}
+
+void MainWindow::appendLogDebug(const QString& text) {
+ if (appConfig().logLevel() >= 4) {
+ m_pLogWindow->appendDebug(text);
+ }
+}
+
+void MainWindow::appendLogError(const QString& text)
+{
+ m_pLogWindow->appendError(text);
+}
+
+void MainWindow::appendLogRaw(const QString& text)
+{
+ foreach(QString line, text.split(QRegExp("\r|\n|\r\n"))) {
+ if (!line.isEmpty()) {
+ m_pLogWindow->appendRaw(line);
+ updateFromLogLine(line);
+ }
+ }
+}
+
+void MainWindow::updateFromLogLine(const QString &line)
+{
+ // TODO: this code makes Andrew cry
+ checkConnected(line);
+ checkFingerprint(line);
+}
+
+void MainWindow::checkConnected(const QString& line)
+{
+ // TODO: implement ipc connection state messages to replace this hack.
+ if (line.contains("started server") ||
+ line.contains("connected to server") ||
+ line.contains("server status: active"))
+ {
+ setBarrierState(barrierConnected);
+
+ if (!appConfig().startedBefore() && isVisible()) {
+ QMessageBox::information(
+ this, "Barrier",
+ tr("Barrier is now connected. You can close the "
+ "config window and Barrier will remain connected in "
+ "the background."));
+
+ appConfig().setStartedBefore(true);
+ appConfig().saveSettings();
+ }
+ }
+}
+
+void MainWindow::checkFingerprint(const QString& line)
+{
+ QRegExp fingerprintRegex(".*server fingerprint: ([A-F0-9:]+)");
+ if (!fingerprintRegex.exactMatch(line)) {
+ return;
+ }
+
+ QString fingerprint = fingerprintRegex.cap(1);
+ if (Fingerprint::trustedServers().isTrusted(fingerprint)) {
+ return;
+ }
+
+ static bool messageBoxAlreadyShown = false;
+
+ if (!messageBoxAlreadyShown) {
+ stopBarrier();
+
+ messageBoxAlreadyShown = true;
+ QMessageBox::StandardButton fingerprintReply =
+ QMessageBox::information(
+ this, tr("Security question"),
+ tr("Do you trust this fingerprint?\n\n"
+ "%1\n\n"
+ "This is a server fingerprint. You should compare this "
+ "fingerprint to the one on your server's screen. If the "
+ "two don't match exactly, then it's probably not the server "
+ "you're expecting (it could be a malicious user).\n\n"
+ "To automatically trust this fingerprint for future "
+ "connections, click Yes. To reject this fingerprint and "
+ "disconnect from the server, click No.")
+ .arg(fingerprint),
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (fingerprintReply == QMessageBox::Yes) {
+ // restart core process after trusting fingerprint.
+ Fingerprint::trustedServers().trust(fingerprint);
+ startBarrier();
+ }
+
+ messageBoxAlreadyShown = false;
+ }
+}
+
+void MainWindow::restartBarrier()
+{
+ stopBarrier();
+ startBarrier();
+}
+
+void MainWindow::proofreadInfo()
+{
+ int oldState = m_BarrierState;
+ m_BarrierState = barrierDisconnected;
+ setBarrierState((qBarrierState)oldState);
+}
+
+void MainWindow::startBarrier()
+{
+ bool desktopMode = appConfig().processMode() == Desktop;
+ bool serviceMode = appConfig().processMode() == Service;
+
+ appendLogDebug("starting process");
+ m_ExpectedRunningState = kStarted;
+ setBarrierState(barrierConnecting);
+
+ QString app;
+ QStringList args;
+
+ args << "-f" << "--no-tray" << "--debug" << appConfig().logLevelText();
+
+
+ args << "--name" << getScreenName();
+
+ if (desktopMode)
+ {
+ setBarrierProcess(new QProcess(this));
+ }
+ else
+ {
+ // tell client/server to talk to daemon through ipc.
+ args << "--ipc";
+
+#if defined(Q_OS_WIN)
+ // tell the client/server to shut down when a ms windows desk
+ // is switched; this is because we may need to elevate or not
+ // based on which desk the user is in (login always needs
+ // elevation, where as default desk does not).
+ // Note that this is only enabled when barrier is set to elevate
+ // 'as needed' (e.g. on a UAC dialog popup) in order to prevent
+ // unnecessary restarts when barrier was started elevated or
+ // when it is not allowed to elevate. In these cases restarting
+ // the server is fruitless.
+ if (appConfig().elevateMode() == ElevateAsNeeded) {
+ args << "--stop-on-desk-switch";
+ }
+#endif
+ }
+
+#ifndef Q_OS_LINUX
+
+ if (m_ServerConfig.enableDragAndDrop()) {
+ args << "--enable-drag-drop";
+ }
+
+#endif
+
+ if (m_AppConfig->getCryptoEnabled()) {
+ args << "--enable-crypto";
+ }
+
+#if defined(Q_OS_WIN)
+ // on windows, the profile directory changes depending on the user that
+ // launched the process (e.g. when launched with elevation). setting the
+ // profile dir on launch ensures it uses the same profile dir is used
+ // no matter how its relaunched.
+ args << "--profile-dir" << getProfileRootForArg();
+#endif
+
+ if ((barrierType() == barrierClient && !clientArgs(args, app))
+ || (barrierType() == barrierServer && !serverArgs(args, app)))
+ {
+ stopBarrier();
+ return;
+ }
+
+ if (desktopMode)
+ {
+ connect(barrierProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(barrierFinished(int, QProcess::ExitStatus)));
+ connect(barrierProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput()));
+ connect(barrierProcess(), SIGNAL(readyReadStandardError()), this, SLOT(logError()));
+ }
+
+ m_pLogWindow->startNewInstance();
+
+ appendLogInfo("starting " + QString(barrierType() == barrierServer ? "server" : "client"));
+
+ qDebug() << args;
+
+ // show command if debug log level...
+ if (appConfig().logLevel() >= 4) {
+ appendLogInfo(QString("command: %1 %2").arg(app, args.join(" ")));
+ }
+
+ appendLogInfo("config file: " + configFilename());
+ appendLogInfo("log level: " + appConfig().logLevelText());
+
+ if (appConfig().logToFile())
+ appendLogInfo("log file: " + appConfig().logFilename());
+
+ if (desktopMode)
+ {
+ barrierProcess()->start(app, args);
+ if (!barrierProcess()->waitForStarted())
+ {
+ show();
+ QMessageBox::warning(this, tr("Program can not be started"), QString(tr("The executable<br><br>%1<br><br>could not be successfully started, although it does exist. Please check if you have sufficient permissions to run this program.").arg(app)));
+ return;
+ }
+ }
+
+ if (serviceMode)
+ {
+ QString command(app + " " + args.join(" "));
+ m_IpcClient.sendCommand(command, appConfig().elevateMode());
+ }
+}
+
+bool MainWindow::clientArgs(QStringList& args, QString& app)
+{
+ app = appPath(appConfig().barriercName());
+
+ if (!QFile::exists(app))
+ {
+ show();
+ QMessageBox::warning(this, tr("Barrier client not found"),
+ tr("The executable for the barrier client does not exist."));
+ return false;
+ }
+
+#if defined(Q_OS_WIN)
+ // wrap in quotes so a malicious user can't start \Program.exe as admin.
+ app = QString("\"%1\"").arg(app);
+#endif
+
+ if (appConfig().logToFile())
+ {
+ appConfig().persistLogDir();
+ args << "--log" << appConfig().logFilenameCmd();
+ }
+
+ // check auto config first, if it is disabled or no server detected,
+ // use line edit host name if it is not empty
+ if (m_pCheckBoxAutoConfig->isChecked()) {
+ if (m_pComboServerList->count() != 0) {
+ QString serverIp = m_pComboServerList->currentText();
+ args << serverIp + ":" + QString::number(appConfig().port());
+ return true;
+ }
+ }
+
+ if (m_pLineEditHostname->text().isEmpty()) {
+ show();
+ if (!m_SuppressEmptyServerWarning) {
+ QMessageBox::warning(this, tr("Hostname is empty"),
+ tr("Please fill in a hostname for the barrier client to connect to."));
+ }
+ return false;
+ }
+
+ args << m_pLineEditHostname->text() + ":" + QString::number(appConfig().port());
+
+ return true;
+}
+
+QString MainWindow::configFilename()
+{
+ QString filename;
+ if (m_pRadioInternalConfig->isChecked())
+ {
+ // TODO: no need to use a temporary file, since we need it to
+ // be permenant (since it'll be used for Windows services, etc).
+ m_pTempConfigFile = new QTemporaryFile();
+ if (!m_pTempConfigFile->open())
+ {
+ QMessageBox::critical(this, tr("Cannot write configuration file"), tr("The temporary configuration file required to start barrier can not be written."));
+ return "";
+ }
+
+ serverConfig().save(*m_pTempConfigFile);
+ filename = m_pTempConfigFile->fileName();
+
+ m_pTempConfigFile->close();
+ }
+ else
+ {
+ if (!QFile::exists(m_pLineEditConfigFile->text()))
+ {
+ if (QMessageBox::warning(this, tr("Configuration filename invalid"),
+ tr("You have not filled in a valid configuration file for the barrier server. "
+ "Do you want to browse for the configuration file now?"), QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes
+ || !on_m_pButtonBrowseConfigFile_clicked())
+ return "";
+ }
+
+ filename = m_pLineEditConfigFile->text();
+ }
+ return filename;
+}
+
+QString MainWindow::address()
+{
+ QString i = appConfig().networkInterface();
+ return (!i.isEmpty() ? i : "") + ":" + QString::number(appConfig().port());
+}
+
+QString MainWindow::appPath(const QString& name)
+{
+ return appConfig().barrierProgramDir() + name;
+}
+
+bool MainWindow::serverArgs(QStringList& args, QString& app)
+{
+ app = appPath(appConfig().barriersName());
+
+ if (!QFile::exists(app))
+ {
+ QMessageBox::warning(this, tr("Barrier server not found"),
+ tr("The executable for the barrier server does not exist."));
+ return false;
+ }
+
+#if defined(Q_OS_WIN)
+ // wrap in quotes so a malicious user can't start \Program.exe as admin.
+ app = QString("\"%1\"").arg(app);
+#endif
+
+ if (appConfig().logToFile())
+ {
+ appConfig().persistLogDir();
+
+ args << "--log" << appConfig().logFilenameCmd();
+ }
+
+ QString configFilename = this->configFilename();
+#if defined(Q_OS_WIN)
+ // wrap in quotes in case username contains spaces.
+ configFilename = QString("\"%1\"").arg(configFilename);
+#endif
+ args << "-c" << configFilename << "--address" << address();
+
+ return true;
+}
+
+void MainWindow::stopBarrier()
+{
+ appendLogDebug("stopping process");
+
+ m_ExpectedRunningState = kStopped;
+
+ if (appConfig().processMode() == Service)
+ {
+ stopService();
+ }
+ else if (appConfig().processMode() == Desktop)
+ {
+ stopDesktop();
+ }
+
+ setBarrierState(barrierDisconnected);
+
+ // HACK: deleting the object deletes the physical file, which is
+ // bad, since it could be in use by the Windows service!
+#if !defined(Q_OS_WIN)
+ delete m_pTempConfigFile;
+#endif
+ m_pTempConfigFile = NULL;
+
+ // reset so that new connects cause auto-hide.
+ m_AlreadyHidden = false;
+}
+
+void MainWindow::stopService()
+{
+ // send empty command to stop service from laucning anything.
+ m_IpcClient.sendCommand("", appConfig().elevateMode());
+}
+
+void MainWindow::stopDesktop()
+{
+ QMutexLocker locker(&m_StopDesktopMutex);
+ if (!barrierProcess()) {
+ return;
+ }
+
+ appendLogInfo("stopping barrier desktop process");
+
+ if (barrierProcess()->isOpen()) {
+ // try to shutdown child gracefully
+ barrierProcess()->write(&ShutdownCh, 1);
+ barrierProcess()->waitForFinished(5000);
+ barrierProcess()->close();
+ }
+
+ delete barrierProcess();
+ setBarrierProcess(NULL);
+}
+
+void MainWindow::barrierFinished(int exitCode, QProcess::ExitStatus)
+{
+ if (exitCode == 0) {
+ appendLogInfo(QString("process exited normally"));
+ }
+ else {
+ appendLogError(QString("process exited with error code: %1").arg(exitCode));
+ }
+
+ if (m_ExpectedRunningState == kStarted) {
+ QTimer::singleShot(1000, this, SLOT(startBarrier()));
+ appendLogInfo(QString("detected process not running, auto restarting"));
+ }
+ else {
+ setBarrierState(barrierDisconnected);
+ }
+}
+
+void MainWindow::setBarrierState(qBarrierState state)
+{
+ if (barrierState() == state)
+ return;
+
+ if (state == barrierConnected || state == barrierConnecting)
+ {
+ disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartBarrier, SLOT(trigger()));
+ connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopBarrier, SLOT(trigger()));
+ m_pButtonToggleStart->setText(tr("&Stop"));
+ m_pButtonApply->setEnabled(true);
+ }
+ else if (state == barrierDisconnected)
+ {
+ disconnect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopBarrier, SLOT(trigger()));
+ connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartBarrier, SLOT(trigger()));
+ m_pButtonToggleStart->setText(tr("&Start"));
+ m_pButtonApply->setEnabled(false);
+ }
+
+ bool connected = false;
+ if (state == barrierConnected || state == barrierTransfering) {
+ connected = true;
+ }
+
+ m_pActionStartBarrier->setEnabled(!connected);
+ m_pActionStopBarrier->setEnabled(connected);
+
+ switch (state)
+ {
+ case barrierConnected: {
+ if (m_AppConfig->getCryptoEnabled()) {
+ m_pLabelPadlock->show();
+ }
+ else {
+ m_pLabelPadlock->hide();
+ }
+
+ setStatus(tr("Barrier is running."));
+
+ break;
+ }
+ case barrierConnecting:
+ m_pLabelPadlock->hide();
+ setStatus(tr("Barrier is starting."));
+ break;
+ case barrierDisconnected:
+ m_pLabelPadlock->hide();
+ setStatus(tr("Barrier is not running."));
+ break;
+ case barrierTransfering:
+ break;
+ }
+
+ setIcon(state);
+
+ m_BarrierState = state;
+}
+
+void MainWindow::setVisible(bool visible)
+{
+ QMainWindow::setVisible(visible);
+ m_pActionMinimize->setEnabled(visible);
+ m_pActionRestore->setEnabled(!visible);
+
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 // lion
+ // dock hide only supported on lion :(
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ GetCurrentProcess(&psn);
+#pragma GCC diagnostic pop
+ if (visible)
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ else
+ TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
+#endif
+}
+
+QString MainWindow::getIPAddresses()
+{
+ QList<QHostAddress> addresses = QNetworkInterface::allAddresses();
+
+ bool hinted = false;
+ QString result;
+ for (int i = 0; i < addresses.size(); i++) {
+ if (addresses[i].protocol() == QAbstractSocket::IPv4Protocol &&
+ addresses[i] != QHostAddress(QHostAddress::LocalHost)) {
+
+ QString address = addresses[i].toString();
+ QString format = "%1, ";
+
+ // usually 192.168.x.x is a useful ip for the user, so indicate
+ // this by making it bold.
+ if (!hinted && address.startsWith("192.168")) {
+ hinted = true;
+ format = "<b>%1</b>, ";
+ }
+
+ result += format.arg(address);
+ }
+ }
+
+ if (result == "") {
+ return tr("Unknown");
+ }
+
+ // remove trailing comma.
+ result.chop(2);
+
+ return result;
+}
+
+QString MainWindow::getScreenName()
+{
+ if (appConfig().screenName() == "") {
+ return QHostInfo::localHostName();
+ }
+ else {
+ return appConfig().screenName();
+ }
+}
+
+void MainWindow::changeEvent(QEvent* event)
+{
+ if (event != 0)
+ {
+ switch (event->type())
+ {
+ case QEvent::LanguageChange:
+ {
+ retranslateUi(this);
+ retranslateMenuBar();
+
+ proofreadInfo();
+
+ break;
+ }
+ case QEvent::WindowStateChange:
+ {
+ windowStateChanged();
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ // all that do not return are allowing the event to propagate
+ QMainWindow::changeEvent(event);
+}
+
+void MainWindow::updateZeroconfService()
+{
+ QMutexLocker locker(&m_UpdateZeroconfMutex);
+
+ if (isBonjourRunning()) {
+ if (!m_AppConfig->wizardShouldRun()) {
+ if (m_pZeroconfService) {
+ delete m_pZeroconfService;
+ m_pZeroconfService = NULL;
+ }
+
+ if (m_AppConfig->autoConfig() || barrierType() == barrierServer) {
+ m_pZeroconfService = new ZeroconfService(this);
+ }
+ }
+ }
+}
+
+void MainWindow::serverDetected(const QString name)
+{
+ if (m_pComboServerList->findText(name) == -1) {
+ // Note: the first added item triggers startBarrier
+ m_pComboServerList->addItem(name);
+ }
+
+ if (m_pComboServerList->count() > 1) {
+ m_pComboServerList->show();
+ }
+}
+
+void MainWindow::updateSSLFingerprint()
+{
+ if (m_AppConfig->getCryptoEnabled() && m_pSslCertificate == nullptr) {
+ m_pSslCertificate = new SslCertificate(this);
+ m_pSslCertificate->generateCertificate();
+ }
+ if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) {
+ m_pLabelLocalFingerprint->setText(Fingerprint::local().readFirst());
+ } else {
+ m_pLabelLocalFingerprint->setText("Disabled");
+ }
+}
+
+void MainWindow::on_m_pGroupClient_toggled(bool on)
+{
+ m_pGroupServer->setChecked(!on);
+ if (on) {
+ updateZeroconfService();
+ }
+}
+
+void MainWindow::on_m_pGroupServer_toggled(bool on)
+{
+ m_pGroupClient->setChecked(!on);
+ if (on) {
+ updateZeroconfService();
+ }
+}
+
+bool MainWindow::on_m_pButtonBrowseConfigFile_clicked()
+{
+ QString fileName = QFileDialog::getOpenFileName(this, tr("Browse for a barriers config file"), QString(), barrierConfigFilter);
+
+ if (!fileName.isEmpty())
+ {
+ m_pLineEditConfigFile->setText(fileName);
+ return true;
+ }
+
+ return false;
+}
+
+bool MainWindow::on_m_pActionSave_triggered()
+{
+ QString fileName = QFileDialog::getSaveFileName(this, tr("Save configuration as..."), QString(), tr("Barrier Configuration (*.sgc)"));
+
+ if (!fileName.isEmpty() && !serverConfig().save(fileName))
+ {
+ QMessageBox::warning(this, tr("Save failed"), tr("Could not save configuration to file."));
+ return true;
+ }
+
+ return false;
+}
+
+void MainWindow::on_m_pActionAbout_triggered()
+{
+ AboutDialog(this, appPath(appConfig().barriercName())).exec();
+}
+
+void MainWindow::on_m_pActionSettings_triggered()
+{
+ if (SettingsDialog(this, appConfig()).exec() == QDialog::Accepted)
+ updateSSLFingerprint();
+}
+
+void MainWindow::autoAddScreen(const QString name)
+{
+ if (!m_ServerConfig.ignoreAutoConfigClient()) {
+ int r = m_ServerConfig.autoAddScreen(name);
+ if (r != kAutoAddScreenOk) {
+ switch (r) {
+ case kAutoAddScreenManualServer:
+ showConfigureServer(
+ tr("Please add the server (%1) to the grid.")
+ .arg(appConfig().screenName()));
+ break;
+
+ case kAutoAddScreenManualClient:
+ showConfigureServer(
+ tr("Please drag the new client screen (%1) "
+ "to the desired position on the grid.")
+ .arg(name));
+ break;
+ }
+ }
+ else {
+ restartBarrier();
+ }
+ }
+}
+
+void MainWindow::showConfigureServer(const QString& message)
+{
+ ServerConfigDialog dlg(this, serverConfig(), appConfig().screenName());
+ dlg.message(message);
+ dlg.exec();
+}
+
+void MainWindow::on_m_pButtonConfigureServer_clicked()
+{
+ showConfigureServer();
+}
+
+void MainWindow::on_m_pButtonApply_clicked()
+{
+ restartBarrier();
+}
+
+#if defined(Q_OS_WIN)
+bool MainWindow::isServiceRunning(QString name)
+{
+ SC_HANDLE hSCManager;
+ hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+ if (hSCManager == NULL) {
+ appendLogError("failed to open a service controller manager, error: " +
+ GetLastError());
+ return false;
+ }
+
+ auto array = name.toLocal8Bit();
+ SC_HANDLE hService = OpenService(hSCManager, array.data(), SERVICE_QUERY_STATUS);
+
+ if (hService == NULL) {
+ appendLogDebug("failed to open service: " + name);
+ return false;
+ }
+
+ SERVICE_STATUS status;
+ if (QueryServiceStatus(hService, &status)) {
+ if (status.dwCurrentState == SERVICE_RUNNING) {
+ return true;
+ }
+ }
+
+ return false;
+}
+#else
+bool MainWindow::isServiceRunning()
+{
+ return false;
+}
+#endif
+
+bool MainWindow::isBonjourRunning()
+{
+ bool result = false;
+
+#if defined(Q_OS_WIN)
+ result = isServiceRunning("Bonjour Service");
+#else
+ result = true;
+#endif
+
+ return result;
+}
+
+void MainWindow::downloadBonjour()
+{
+#if defined(Q_OS_WIN)
+ QUrl url;
+ int arch = getProcessorArch();
+ if (arch == kProcessorArchWin32) {
+ url.setUrl(bonjourBaseUrl + bonjourFilename32);
+ appendLogInfo("downloading 32-bit Bonjour");
+ }
+ else if (arch == kProcessorArchWin64) {
+ url.setUrl(bonjourBaseUrl + bonjourFilename64);
+ appendLogInfo("downloading 64-bit Bonjour");
+ }
+ else {
+ QMessageBox::critical(
+ this, tr("Barrier"),
+ tr("Failed to detect system architecture."));
+ return;
+ }
+
+ if (m_pDataDownloader == NULL) {
+ m_pDataDownloader = new DataDownloader(this);
+ connect(m_pDataDownloader, SIGNAL(isComplete()), SLOT(installBonjour()));
+ }
+
+ m_pDataDownloader->download(url);
+
+ if (m_DownloadMessageBox == NULL) {
+ m_DownloadMessageBox = new QMessageBox(this);
+ m_DownloadMessageBox->setWindowTitle("Barrier");
+ m_DownloadMessageBox->setIcon(QMessageBox::Information);
+ m_DownloadMessageBox->setText("Installing Bonjour, please wait...");
+ m_DownloadMessageBox->setStandardButtons(0);
+ m_pCancelButton = m_DownloadMessageBox->addButton(
+ tr("Cancel"), QMessageBox::RejectRole);
+ }
+
+ m_DownloadMessageBox->exec();
+
+ if (m_DownloadMessageBox->clickedButton() == m_pCancelButton) {
+ m_pDataDownloader->cancel();
+ }
+#endif
+}
+
+void MainWindow::installBonjour()
+{
+#if defined(Q_OS_WIN)
+#if QT_VERSION >= 0x050000
+ QString tempLocation = QStandardPaths::writableLocation(QStandardPaths::TempLocation);
+#else
+ QString tempLocation = QDesktopServices::storageLocation(
+ QDesktopServices::TempLocation);
+#endif
+ QString filename = tempLocation;
+ filename.append("\\").append(bonjourTargetFilename);
+ QFile file(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ m_DownloadMessageBox->hide();
+
+ QMessageBox::warning(
+ this, "Barrier",
+ tr("Failed to download Bonjour installer to location: %1")
+ .arg(tempLocation));
+ return;
+ }
+
+ file.write(m_pDataDownloader->data());
+ file.close();
+
+ QStringList arguments;
+ arguments.append("/i");
+ QString winFilename = QDir::toNativeSeparators(filename);
+ arguments.append(winFilename);
+ arguments.append("/passive");
+ if (m_BonjourInstall == NULL) {
+ m_BonjourInstall = new CommandProcess("msiexec", arguments);
+ }
+
+ QThread* thread = new QThread;
+ connect(m_BonjourInstall, SIGNAL(finished()), this,
+ SLOT(bonjourInstallFinished()));
+ connect(m_BonjourInstall, SIGNAL(finished()), thread, SLOT(quit()));
+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+
+ m_BonjourInstall->moveToThread(thread);
+ thread->start();
+
+ QMetaObject::invokeMethod(m_BonjourInstall, "run", Qt::QueuedConnection);
+
+ m_DownloadMessageBox->hide();
+#endif
+}
+
+void MainWindow::promptAutoConfig()
+{
+ if (!isBonjourRunning()) {
+ int r = QMessageBox::question(
+ this, tr("Barrier"),
+ tr("Do you want to enable auto config and install Bonjour?\n\n"
+ "This feature helps you establish the connection."),
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (r == QMessageBox::Yes) {
+ m_AppConfig->setAutoConfig(true);
+ downloadBonjour();
+ }
+ else {
+ m_AppConfig->setAutoConfig(false);
+ m_pCheckBoxAutoConfig->setChecked(false);
+ }
+ }
+
+ m_AppConfig->setAutoConfigPrompted(true);
+}
+
+void MainWindow::on_m_pComboServerList_currentIndexChanged(QString )
+{
+ if (m_pComboServerList->count() != 0) {
+ restartBarrier();
+ }
+}
+
+void MainWindow::on_m_pCheckBoxAutoConfig_toggled(bool checked)
+{
+ if (!isBonjourRunning() && checked) {
+ if (!m_SuppressAutoConfigWarning) {
+ int r = QMessageBox::information(
+ this, tr("Barrier"),
+ tr("Auto config feature requires Bonjour.\n\n"
+ "Do you want to install Bonjour?"),
+ QMessageBox::Yes | QMessageBox::No);
+
+ if (r == QMessageBox::Yes) {
+ downloadBonjour();
+ }
+ }
+
+ m_pCheckBoxAutoConfig->setChecked(false);
+ return;
+ }
+
+ m_pLineEditHostname->setDisabled(checked);
+ appConfig().setAutoConfig(checked);
+ updateZeroconfService();
+
+ if (!checked) {
+ m_pComboServerList->clear();
+ m_pComboServerList->hide();
+ }
+}
+
+void MainWindow::bonjourInstallFinished()
+{
+ appendLogInfo("Bonjour install finished");
+
+ m_pCheckBoxAutoConfig->setChecked(true);
+}
+
+QString MainWindow::getProfileRootForArg()
+{
+ CoreInterface coreInterface;
+ QString dir = coreInterface.getProfileDir();
+
+ // HACK: strip our app name since we're returning the root dir.
+#if defined(Q_OS_WIN)
+ dir.replace("\\Barrier", "");
+#else
+ dir.replace("/.barrier", "");
+#endif
+
+ return QString("\"%1\"").arg(dir);
+}
+
+void MainWindow::windowStateChanged()
+{
+ if (windowState() == Qt::WindowMinimized && appConfig().getMinimizeToTray())
+ hide();
+}
+
+void MainWindow::showLogWindow()
+{
+ m_pLogWindow->show();
+}
diff --git a/src/gui/src/MainWindow.h b/src/gui/src/MainWindow.h
new file mode 100644
index 0000000..27b30d1
--- /dev/null
+++ b/src/gui/src/MainWindow.h
@@ -0,0 +1,215 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(MAINWINDOW__H)
+
+#define MAINWINDOW__H
+
+#include <QMainWindow>
+#include <QSystemTrayIcon>
+#include <QSettings>
+#include <QProcess>
+#include <QThread>
+
+#include "ui_MainWindowBase.h"
+
+#include "ServerConfig.h"
+#include "AppConfig.h"
+#include "VersionChecker.h"
+#include "IpcClient.h"
+#include "Ipc.h"
+#include "LogWindow.h"
+
+#include <QMutex>
+
+class QAction;
+class QMenu;
+class QLineEdit;
+class QGroupBox;
+class QPushButton;
+class QTextEdit;
+class QComboBox;
+class QTabWidget;
+class QCheckBox;
+class QRadioButton;
+class QTemporaryFile;
+class QMessageBox;
+class QAbstractButton;
+
+class LogDialog;
+class QBarrierApplication;
+class SetupWizard;
+class ZeroconfService;
+class DataDownloader;
+class CommandProcess;
+class SslCertificate;
+
+class MainWindow : public QMainWindow, public Ui::MainWindowBase
+{
+ Q_OBJECT
+
+ friend class QBarrierApplication;
+ friend class SetupWizard;
+ friend class SettingsDialog;
+
+ public:
+ enum qBarrierState
+ {
+ barrierDisconnected,
+ barrierConnecting,
+ barrierConnected,
+ barrierTransfering
+ };
+
+ enum qBarrierType
+ {
+ barrierClient,
+ barrierServer
+ };
+
+ enum qLevel {
+ Error,
+ Info
+ };
+
+ enum qRuningState {
+ kStarted,
+ kStopped
+ };
+
+ public:
+ MainWindow(QSettings& settings, AppConfig& appConfig);
+ ~MainWindow();
+
+ public:
+ void setVisible(bool visible);
+ int barrierType() const { return m_pGroupClient->isChecked() ? barrierClient : barrierServer; }
+ int barrierState() const { return m_BarrierState; }
+ QString hostname() const { return m_pLineEditHostname->text(); }
+ QString configFilename();
+ QString address();
+ QString appPath(const QString& name);
+ void open();
+ VersionChecker& versionChecker() { return m_VersionChecker; }
+ QString getScreenName();
+ ServerConfig& serverConfig() { return m_ServerConfig; }
+ void showConfigureServer(const QString& message);
+ void showConfigureServer() { showConfigureServer(""); }
+ void autoAddScreen(const QString name);
+ void updateZeroconfService();
+ void serverDetected(const QString name);
+
+public slots:
+ void appendLogRaw(const QString& text);
+ void appendLogInfo(const QString& text);
+ void appendLogDebug(const QString& text);
+ void appendLogError(const QString& text);
+ void startBarrier();
+
+ protected slots:
+ void on_m_pGroupClient_toggled(bool on);
+ void on_m_pGroupServer_toggled(bool on);
+ bool on_m_pButtonBrowseConfigFile_clicked();
+ void on_m_pButtonConfigureServer_clicked();
+ bool on_m_pActionSave_triggered();
+ void on_m_pActionAbout_triggered();
+ void on_m_pActionSettings_triggered();
+ void barrierFinished(int exitCode, QProcess::ExitStatus);
+ void trayActivated(QSystemTrayIcon::ActivationReason reason);
+ void stopBarrier();
+ void logOutput();
+ void logError();
+ void bonjourInstallFinished();
+ void showLogWindow();
+
+ protected:
+ QSettings& settings() { return m_Settings; }
+ AppConfig& appConfig() { return *m_AppConfig; }
+ QProcess* barrierProcess() { return m_pBarrier; }
+ void setBarrierProcess(QProcess* p) { m_pBarrier = p; }
+ void initConnections();
+ void createMenuBar();
+ void createTrayIcon();
+ void loadSettings();
+ void saveSettings();
+ void setIcon(qBarrierState state);
+ void setBarrierState(qBarrierState state);
+ bool clientArgs(QStringList& args, QString& app);
+ bool serverArgs(QStringList& args, QString& app);
+ void setStatus(const QString& status);
+ void updateFromLogLine(const QString& line);
+ QString getIPAddresses();
+ void stopService();
+ void stopDesktop();
+ void changeEvent(QEvent* event);
+ void retranslateMenuBar();
+#if defined(Q_OS_WIN)
+ bool isServiceRunning(QString name);
+#else
+ bool isServiceRunning();
+#endif
+ bool isBonjourRunning();
+ void downloadBonjour();
+ void promptAutoConfig();
+ QString getProfileRootForArg();
+ void checkConnected(const QString& line);
+ void checkFingerprint(const QString& line);
+ void restartBarrier();
+ void proofreadInfo();
+ void windowStateChanged();
+ void updateSSLFingerprint();
+
+ private:
+ QSettings& m_Settings;
+ AppConfig* m_AppConfig;
+ QProcess* m_pBarrier;
+ int m_BarrierState;
+ ServerConfig m_ServerConfig;
+ QTemporaryFile* m_pTempConfigFile;
+ QSystemTrayIcon* m_pTrayIcon;
+ QMenu* m_pTrayIconMenu;
+ bool m_AlreadyHidden;
+ VersionChecker m_VersionChecker;
+ IpcClient m_IpcClient;
+ QMenuBar* m_pMenuBar;
+ QMenu* m_pMenuBarrier;
+ QMenu* m_pMenuHelp;
+ ZeroconfService* m_pZeroconfService;
+ DataDownloader* m_pDataDownloader;
+ QMessageBox* m_DownloadMessageBox;
+ QAbstractButton* m_pCancelButton;
+ QMutex m_UpdateZeroconfMutex;
+ bool m_SuppressAutoConfigWarning;
+ CommandProcess* m_BonjourInstall;
+ bool m_SuppressEmptyServerWarning;
+ qRuningState m_ExpectedRunningState;
+ QMutex m_StopDesktopMutex;
+ SslCertificate* m_pSslCertificate;
+ QStringList m_PendingClientNames;
+ LogWindow *m_pLogWindow;
+
+private slots:
+ void on_m_pCheckBoxAutoConfig_toggled(bool checked);
+ void on_m_pComboServerList_currentIndexChanged(QString );
+ void on_m_pButtonApply_clicked();
+ void installBonjour();
+
+};
+
+#endif
+
diff --git a/src/gui/src/MainWindowBase.ui b/src/gui/src/MainWindowBase.ui
new file mode 100644
index 0000000..d1b0b37
--- /dev/null
+++ b/src/gui/src/MainWindowBase.ui
@@ -0,0 +1,470 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindowBase</class>
+ <widget class="QMainWindow" name="MainWindowBase">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>550</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>600</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Barrier</string>
+ </property>
+ <widget class="QWidget" name="centralwidget">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QGroupBox" name="m_pGroupServer">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Ser&amp;ver (share this computer's mouse and keyboard):</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>IP addresses:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="m_pLabelIpAddresses">
+ <property name="text">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="m_pLabelFingerprint">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>SSL Fingerprint:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_pLabelLocalFingerprint">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="m_pRadioInternalConfig">
+ <property name="text">
+ <string>Configure interactively:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QPushButton" name="m_pButtonConfigureServer">
+ <property name="text">
+ <string>&amp;Configure Server...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="m_pRadioExternalConfig">
+ <property name="text">
+ <string>Use existing configuration:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="m_pLabelConfigurationFile">
+ <property name="text">
+ <string>&amp;Configuration file:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pLineEditConfigFile</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="m_pLineEditConfigFile">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_pButtonBrowseConfigFile">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Browse...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="m_pGroupClient">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>&amp;Client (use another computer's mouse and keyboard):</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <layout class="QFormLayout" name="formLayout_3">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>Screen name:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="m_pLabelScreenName">
+ <property name="text">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="m_pLabelServerName">
+ <property name="text">
+ <string>&amp;Server IP:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pLineEditHostname</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="m_pLineEditHostname"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxAutoConfig">
+ <property name="text">
+ <string>Auto config</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="m_pComboServerList">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>120</width>
+ <height>0</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <item>
+ <widget class="QLabel" name="m_pLabelPadlock">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="Barrier.qrc">:/res/icons/16x16/padlock.png</pixmap>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_pStatusLabel">
+ <property name="text">
+ <string>Ready</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="spacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_pButtonApply">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Apply</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="m_pButtonToggleStart">
+ <property name="text">
+ <string>&amp;Start</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <action name="m_pActionAbout">
+ <property name="text">
+ <string>&amp;About Barrier...</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true"/>
+ </property>
+ </action>
+ <action name="m_pActionQuit">
+ <property name="text">
+ <string>&amp;Quit</string>
+ </property>
+ <property name="statusTip">
+ <string>Quit</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">Ctrl+Q</string>
+ </property>
+ </action>
+ <action name="m_pActionStartBarrier">
+ <property name="text">
+ <string>&amp;Start</string>
+ </property>
+ <property name="statusTip">
+ <string>Run</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">Ctrl+S</string>
+ </property>
+ </action>
+ <action name="m_pActionStopBarrier">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>S&amp;top</string>
+ </property>
+ <property name="statusTip">
+ <string>Stop</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">Ctrl+T</string>
+ </property>
+ </action>
+ <action name="m_pActionMinimize">
+ <property name="text">
+ <string>&amp;Hide</string>
+ </property>
+ <property name="toolTip">
+ <string>Hide</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">F5</string>
+ </property>
+ </action>
+ <action name="m_pActionRestore">
+ <property name="text">
+ <string>&amp;Show</string>
+ </property>
+ <property name="toolTip">
+ <string>Show</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true"/>
+ </property>
+ </action>
+ <action name="m_pActionSave">
+ <property name="text">
+ <string>S&amp;ave configuration</string>
+ </property>
+ <property name="statusTip">
+ <string>Save the interactively generated server configuration to a file.</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">Ctrl+Alt+S</string>
+ </property>
+ </action>
+ <action name="m_pActionSettings">
+ <property name="text">
+ <string>Change &amp;Settings</string>
+ </property>
+ <property name="statusTip">
+ <string>Edit settings</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">F4</string>
+ </property>
+ </action>
+ <action name="m_pActionShowLog">
+ <property name="text">
+ <string>Show &amp;Log</string>
+ </property>
+ <property name="toolTip">
+ <string>Show Log</string>
+ </property>
+ <property name="shortcut">
+ <string notr="true">F2</string>
+ </property>
+ </action>
+ </widget>
+ <resources>
+ <include location="Barrier.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>m_pRadioExternalConfig</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pLineEditConfigFile</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>156</x>
+ <y>179</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>169</x>
+ <y>209</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioExternalConfig</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pButtonBrowseConfigFile</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>353</x>
+ <y>182</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>356</x>
+ <y>211</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pRadioInternalConfig</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pButtonConfigureServer</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>204</x>
+ <y>244</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>212</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pButtonToggleStart</sender>
+ <signal>clicked()</signal>
+ <receiver>m_pActionStartBarrier</receiver>
+ <slot>trigger()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>361</x>
+ <y>404</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>-1</x>
+ <y>-1</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/src/NewScreenWidget.cpp b/src/gui/src/NewScreenWidget.cpp
new file mode 100644
index 0000000..18379c0
--- /dev/null
+++ b/src/gui/src/NewScreenWidget.cpp
@@ -0,0 +1,48 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "NewScreenWidget.h"
+#include "ScreenSetupModel.h"
+
+#include <QtCore>
+#include <QtGui>
+
+NewScreenWidget::NewScreenWidget(QWidget* parent) :
+ QLabel(parent)
+{
+}
+
+void NewScreenWidget::mousePressEvent(QMouseEvent* event)
+{
+ Screen newScreen(tr("Unnamed"));
+
+ QByteArray itemData;
+ QDataStream dataStream(&itemData, QIODevice::WriteOnly);
+ dataStream << -1 << -1 << newScreen;
+
+ QMimeData* pMimeData = new QMimeData;
+ pMimeData->setData(ScreenSetupModel::mimeType(), itemData);
+
+ QDrag* pDrag = new QDrag(this);
+ pDrag->setMimeData(pMimeData);
+ pDrag->setPixmap(*pixmap());
+ pDrag->setHotSpot(event->pos());
+
+ pDrag->exec(Qt::CopyAction, Qt::CopyAction);
+}
+
diff --git a/src/gui/src/NewScreenWidget.h b/src/gui/src/NewScreenWidget.h
new file mode 100644
index 0000000..34f3269
--- /dev/null
+++ b/src/gui/src/NewScreenWidget.h
@@ -0,0 +1,40 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(NEWSCREENWIDGET__H)
+
+#define NEWSCREENWIDGET__H
+
+#include <QLabel>
+
+class QMouseEvent;
+class QWidget;
+
+class NewScreenWidget : public QLabel
+{
+ Q_OBJECT
+
+ public:
+ NewScreenWidget(QWidget* parent);
+
+ protected:
+ void mousePressEvent(QMouseEvent* event);
+};
+
+#endif
+
diff --git a/src/gui/src/ProcessorArch.h b/src/gui/src/ProcessorArch.h
new file mode 100644
index 0000000..dd686ce
--- /dev/null
+++ b/src/gui/src/ProcessorArch.h
@@ -0,0 +1,28 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+enum qProcessorArch {
+ kProcessorArchWin32,
+ kProcessorArchWin64,
+ kProcessorArchMac32,
+ kProcessorArchMac64,
+ kProcessorArchLinux32,
+ kProcessorArchLinux64,
+ kProcessorArchUnknown
+};
diff --git a/src/gui/src/QBarrierApplication.cpp b/src/gui/src/QBarrierApplication.cpp
new file mode 100644
index 0000000..f2da382
--- /dev/null
+++ b/src/gui/src/QBarrierApplication.cpp
@@ -0,0 +1,72 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "QBarrierApplication.h"
+#include "MainWindow.h"
+
+#include <QtCore>
+#include <QtGui>
+
+QBarrierApplication* QBarrierApplication::s_Instance = NULL;
+
+QBarrierApplication::QBarrierApplication(int& argc, char** argv) :
+ QApplication(argc, argv),
+ m_Translator(NULL)
+{
+ s_Instance = this;
+}
+
+QBarrierApplication::~QBarrierApplication()
+{
+ delete m_Translator;
+}
+
+void QBarrierApplication::commitData(QSessionManager&)
+{
+ foreach(QWidget* widget, topLevelWidgets())
+ {
+ MainWindow* mainWindow = qobject_cast<MainWindow*>(widget);
+ if (mainWindow)
+ mainWindow->saveSettings();
+ }
+}
+
+QBarrierApplication* QBarrierApplication::getInstance()
+{
+ return s_Instance;
+}
+
+void QBarrierApplication::switchTranslator(QString lang)
+{
+ if (m_Translator != NULL)
+ {
+ removeTranslator(m_Translator);
+ delete m_Translator;
+ }
+
+ QResource locale(":/res/lang/gui_" + lang + ".qm");
+ m_Translator = new QTranslator();
+ m_Translator->load(locale.data(), locale.size());
+ installTranslator(m_Translator);
+}
+
+void QBarrierApplication::setTranslator(QTranslator* translator)
+{
+ m_Translator = translator;
+ installTranslator(m_Translator);
+}
diff --git a/src/gui/src/QBarrierApplication.h b/src/gui/src/QBarrierApplication.h
new file mode 100644
index 0000000..95729b2
--- /dev/null
+++ b/src/gui/src/QBarrierApplication.h
@@ -0,0 +1,47 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(QBARRIERAPPLICATION__H)
+
+#define QBARRIERAPPLICATION__H
+
+#include <QApplication>
+
+class QSessionManager;
+
+class QBarrierApplication : public QApplication
+{
+ public:
+ QBarrierApplication(int& argc, char** argv);
+ ~QBarrierApplication();
+
+ public:
+ void commitData(QSessionManager& manager);
+ void switchTranslator(QString lang);
+ void setTranslator(QTranslator* translator);
+
+ static QBarrierApplication* getInstance();
+
+ private:
+ QTranslator* m_Translator;
+
+ static QBarrierApplication* s_Instance;
+};
+
+#endif
+
diff --git a/src/gui/src/QUtility.cpp b/src/gui/src/QUtility.cpp
new file mode 100644
index 0000000..7757adf
--- /dev/null
+++ b/src/gui/src/QUtility.cpp
@@ -0,0 +1,115 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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 "QUtility.h"
+
+#include "ProcessorArch.h"
+#include "CommandProcess.h"
+
+#if defined(Q_OS_LINUX)
+#include <QProcess>
+#endif
+
+#if defined(Q_OS_WIN)
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#endif
+
+void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData)
+{
+ for (int i = 0; i < comboBox->count(); ++i)
+ {
+ if (comboBox->itemData(i) == itemData)
+ {
+ comboBox->setCurrentIndex(i);
+ return;
+ }
+ }
+}
+
+QString hash(const QString& string)
+{
+ QByteArray data = string.toUtf8();
+ QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Md5);
+ return hash.toHex();
+}
+
+QString getFirstMacAddress()
+{
+ QString mac;
+ foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces())
+ {
+ mac = interface.hardwareAddress();
+ if (mac.size() != 0)
+ {
+ break;
+ }
+ }
+ return mac;
+}
+
+qProcessorArch getProcessorArch()
+{
+#if defined(Q_OS_WIN)
+ SYSTEM_INFO systemInfo;
+ GetNativeSystemInfo(&systemInfo);
+
+ switch (systemInfo.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ return kProcessorArchWin32;
+ case PROCESSOR_ARCHITECTURE_IA64:
+ return kProcessorArchWin64;
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ return kProcessorArchWin64;
+ default:
+ return kProcessorArchUnknown;
+ }
+#endif
+
+#if defined(Q_OS_LINUX)
+#ifdef __i386__
+ return kProcessorArchLinux32;
+#else
+ return kProcessorArchLinux64;
+#endif
+#endif
+
+ return kProcessorArchUnknown;
+}
+
+QString getOSInformation()
+{
+ QString result;
+
+#if defined(Q_OS_LINUX)
+ result = "Linux";
+ try {
+ QStringList arguments;
+ arguments.append("/etc/os-release");
+ CommandProcess cp("/bin/cat", arguments);
+ QString output = cp.run();
+
+ QRegExp resultRegex(".*PRETTY_NAME=\"([^\"]+)\".*");
+ if (resultRegex.exactMatch(output)) {
+ result = resultRegex.cap(1);
+ }
+ } catch (...) {
+ }
+#endif
+
+ return result;
+}
diff --git a/src/gui/src/QUtility.h b/src/gui/src/QUtility.h
new file mode 100644
index 0000000..62a6082
--- /dev/null
+++ b/src/gui/src/QUtility.h
@@ -0,0 +1,31 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#include "ProcessorArch.h"
+
+#include <QComboBox>
+#include <QVariant>
+#include <QCryptographicHash>
+#include <QNetworkInterface>
+
+void setIndexFromItemData(QComboBox* comboBox, const QVariant& itemData);
+QString hash(const QString& string);
+QString getFirstMacAddress();
+qProcessorArch getProcessorArch();
+QString getOSInformation();
diff --git a/src/gui/src/Screen.cpp b/src/gui/src/Screen.cpp
new file mode 100644
index 0000000..880e78c
--- /dev/null
+++ b/src/gui/src/Screen.cpp
@@ -0,0 +1,147 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "Screen.h"
+
+#include <QtCore>
+#include <QtGui>
+
+Screen::Screen() :
+ m_Pixmap(QPixmap(":res/icons/64x64/video-display.png")),
+ m_Swapped(false)
+{
+ init();
+}
+
+Screen::Screen(const QString& name) :
+ m_Pixmap(QPixmap(":res/icons/64x64/video-display.png")),
+ m_Swapped(false)
+{
+ init();
+ setName(name);
+}
+
+void Screen::init()
+{
+ name().clear();
+ aliases().clear();
+ modifiers().clear();
+ switchCorners().clear();
+ fixes().clear();
+ setSwitchCornerSize(0);
+
+ // m_Modifiers, m_SwitchCorners and m_Fixes are QLists we use like fixed-size arrays,
+ // thus we need to make sure to fill them with the required number of elements.
+ for (int i = 0; i < NumModifiers; i++)
+ modifiers() << i;
+
+ for (int i = 0; i < NumSwitchCorners; i++)
+ switchCorners() << false;
+
+ for (int i = 0; i < NumFixes; i++)
+ fixes() << false;
+}
+
+void Screen::loadSettings(QSettings& settings)
+{
+ setName(settings.value("name").toString());
+
+ if (name().isEmpty())
+ return;
+
+ setSwitchCornerSize(settings.value("switchCornerSize").toInt());
+
+ readSettings(settings, aliases(), "alias", QString(""));
+ readSettings(settings, modifiers(), "modifier", static_cast<int>(DefaultMod), NumModifiers);
+ readSettings(settings, switchCorners(), "switchCorner", false, NumSwitchCorners);
+ readSettings(settings, fixes(), "fix", false, NumFixes);
+}
+
+void Screen::saveSettings(QSettings& settings) const
+{
+ settings.setValue("name", name());
+
+ if (name().isEmpty())
+ return;
+
+ settings.setValue("switchCornerSize", switchCornerSize());
+
+ writeSettings(settings, aliases(), "alias");
+ writeSettings(settings, modifiers(), "modifier");
+ writeSettings(settings, switchCorners(), "switchCorner");
+ writeSettings(settings, fixes(), "fix");
+}
+
+QTextStream& Screen::writeScreensSection(QTextStream& outStream) const
+{
+ outStream << "\t" << name() << ":" << endl;
+
+ for (int i = 0; i < modifiers().size(); i++)
+ if (modifier(i) != i)
+ outStream << "\t\t" << modifierName(i) << " = " << modifierName(modifier(i)) << endl;
+
+ for (int i = 0; i < fixes().size(); i++)
+ outStream << "\t\t" << fixName(i) << " = " << (fixes()[i] ? "true" : "false") << endl;
+
+ outStream << "\t\t" << "switchCorners = none ";
+ for (int i = 0; i < switchCorners().size(); i++)
+ if (switchCorners()[i])
+ outStream << "+" << switchCornerName(i) << " ";
+ outStream << endl;
+
+ outStream << "\t\t" << "switchCornerSize = " << switchCornerSize() << endl;
+
+ return outStream;
+}
+
+QTextStream& Screen::writeAliasesSection(QTextStream& outStream) const
+{
+ if (!aliases().isEmpty())
+ {
+ outStream << "\t" << name() << ":" << endl;
+
+ foreach (const QString& alias, aliases())
+ outStream << "\t\t" << alias << endl;
+ }
+
+ return outStream;
+}
+
+QDataStream& operator<<(QDataStream& outStream, const Screen& screen)
+{
+ return outStream
+ << screen.name()
+ << screen.switchCornerSize()
+ << screen.aliases()
+ << screen.modifiers()
+ << screen.switchCorners()
+ << screen.fixes()
+ ;
+}
+
+QDataStream& operator>>(QDataStream& inStream, Screen& screen)
+{
+ return inStream
+ >> screen.m_Name
+ >> screen.m_SwitchCornerSize
+ >> screen.m_Aliases
+ >> screen.m_Modifiers
+ >> screen.m_SwitchCorners
+ >> screen.m_Fixes
+ ;
+}
diff --git a/src/gui/src/Screen.h b/src/gui/src/Screen.h
new file mode 100644
index 0000000..6d9c668
--- /dev/null
+++ b/src/gui/src/Screen.h
@@ -0,0 +1,105 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(SCREEN__H)
+
+#define SCREEN__H
+
+#include <QPixmap>
+#include <QString>
+#include <QList>
+#include <QStringList>
+
+#include "BaseConfig.h"
+
+class QSettings;
+class QTextStream;
+
+class ScreenSettingsDialog;
+
+class Screen : public BaseConfig
+{
+ friend QDataStream& operator<<(QDataStream& outStream, const Screen& screen);
+ friend QDataStream& operator>>(QDataStream& inStream, Screen& screen);
+ friend class ScreenSettingsDialog;
+ friend class ScreenSetupModel;
+ friend class ScreenSetupView;
+
+ public:
+ Screen();
+ Screen(const QString& name);
+
+ public:
+ const QPixmap* pixmap() const { return &m_Pixmap; }
+ const QString& name() const { return m_Name; }
+ const QStringList& aliases() const { return m_Aliases; }
+
+ bool isNull() const { return m_Name.isEmpty(); }
+ int modifier(int m) const { return m_Modifiers[m] == DefaultMod ? m : m_Modifiers[m]; }
+ const QList<int>& modifiers() const { return m_Modifiers; }
+ bool switchCorner(int c) const { return m_SwitchCorners[c]; }
+ const QList<bool>& switchCorners() const { return m_SwitchCorners; }
+ int switchCornerSize() const { return m_SwitchCornerSize; }
+ bool fix(Fix f) const { return m_Fixes[f]; }
+ const QList<bool>& fixes() const { return m_Fixes; }
+
+ void loadSettings(QSettings& settings);
+ void saveSettings(QSettings& settings) const;
+ QTextStream& writeScreensSection(QTextStream& outStream) const;
+ QTextStream& writeAliasesSection(QTextStream& outStream) const;
+
+ bool swapped() const { return m_Swapped; }
+ QString& name() { return m_Name; }
+ void setName(const QString& name) { m_Name = name; }
+
+ protected:
+ void init();
+ QPixmap* pixmap() { return &m_Pixmap; }
+
+ void setPixmap(const QPixmap& pixmap) { m_Pixmap = pixmap; }
+ QStringList& aliases() { return m_Aliases; }
+ void setModifier(int m, int n) { m_Modifiers[m] = n; }
+ QList<int>& modifiers() { return m_Modifiers; }
+ void addAlias(const QString& alias) { m_Aliases.append(alias); }
+ void setSwitchCorner(int c, bool on) { m_SwitchCorners[c] = on; }
+ QList<bool>& switchCorners() { return m_SwitchCorners; }
+ void setSwitchCornerSize(int val) { m_SwitchCornerSize = val; }
+ void setFix(int f, bool on) { m_Fixes[f] = on; }
+ QList<bool>& fixes() { return m_Fixes; }
+ void setSwapped(bool on) { m_Swapped = on; }
+
+ private:
+ QPixmap m_Pixmap;
+ QString m_Name;
+
+ QStringList m_Aliases;
+ QList<int> m_Modifiers;
+ QList<bool> m_SwitchCorners;
+ int m_SwitchCornerSize;
+ QList<bool> m_Fixes;
+
+ bool m_Swapped;
+};
+
+typedef QList<Screen> ScreenList;
+
+QDataStream& operator<<(QDataStream& outStream, const Screen& screen);
+QDataStream& operator>>(QDataStream& inStream, Screen& screen);
+
+#endif
+
diff --git a/src/gui/src/ScreenSettingsDialog.cpp b/src/gui/src/ScreenSettingsDialog.cpp
new file mode 100644
index 0000000..1e95a9c
--- /dev/null
+++ b/src/gui/src/ScreenSettingsDialog.cpp
@@ -0,0 +1,137 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "ScreenSettingsDialog.h"
+#include "Screen.h"
+
+#include <QtCore>
+#include <QtGui>
+#include <QMessageBox>
+
+ScreenSettingsDialog::ScreenSettingsDialog(QWidget* parent, Screen* pScreen) :
+ QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
+ Ui::ScreenSettingsDialogBase(),
+ m_pScreen(pScreen)
+{
+ setupUi(this);
+
+ QRegExp validScreenName("[a-z0-9\\._-]{,255}", Qt::CaseInsensitive);
+
+ m_pLineEditName->setText(m_pScreen->name());
+ m_pLineEditName->setValidator(new QRegExpValidator(validScreenName, m_pLineEditName));
+ m_pLineEditName->selectAll();
+
+ m_pLineEditAlias->setValidator(new QRegExpValidator(validScreenName, m_pLineEditName));
+
+ for (int i = 0; i < m_pScreen->aliases().count(); i++)
+ new QListWidgetItem(m_pScreen->aliases()[i], m_pListAliases);
+
+ m_pComboBoxShift->setCurrentIndex(m_pScreen->modifier(Screen::Shift));
+ m_pComboBoxCtrl->setCurrentIndex(m_pScreen->modifier(Screen::Ctrl));
+ m_pComboBoxAlt->setCurrentIndex(m_pScreen->modifier(Screen::Alt));
+ m_pComboBoxMeta->setCurrentIndex(m_pScreen->modifier(Screen::Meta));
+ m_pComboBoxSuper->setCurrentIndex(m_pScreen->modifier(Screen::Super));
+
+ m_pCheckBoxCornerTopLeft->setChecked(m_pScreen->switchCorner(Screen::TopLeft));
+ m_pCheckBoxCornerTopRight->setChecked(m_pScreen->switchCorner(Screen::TopRight));
+ m_pCheckBoxCornerBottomLeft->setChecked(m_pScreen->switchCorner(Screen::BottomLeft));
+ m_pCheckBoxCornerBottomRight->setChecked(m_pScreen->switchCorner(Screen::BottomRight));
+ m_pSpinBoxSwitchCornerSize->setValue(m_pScreen->switchCornerSize());
+
+ m_pCheckBoxCapsLock->setChecked(m_pScreen->fix(Screen::CapsLock));
+ m_pCheckBoxNumLock->setChecked(m_pScreen->fix(Screen::NumLock));
+ m_pCheckBoxScrollLock->setChecked(m_pScreen->fix(Screen::ScrollLock));
+ m_pCheckBoxXTest->setChecked(m_pScreen->fix(Screen::XTest));
+}
+
+void ScreenSettingsDialog::accept()
+{
+ if (m_pLineEditName->text().isEmpty())
+ {
+ QMessageBox::warning(
+ this, tr("Screen name is empty"),
+ tr("The screen name cannot be empty. "
+ "Please either fill in a name or cancel the dialog."));
+ return;
+ }
+
+ m_pScreen->init();
+
+ m_pScreen->setName(m_pLineEditName->text());
+
+ for (int i = 0; i < m_pListAliases->count(); i++)
+ {
+ QString alias(m_pListAliases->item(i)->text());
+ if (alias == m_pLineEditName->text())
+ {
+ QMessageBox::warning(
+ this, tr("Screen name matches alias"),
+ tr("The screen name cannot be the same as an alias. "
+ "Please either remove the alias or change the screen name."));
+ return;
+ }
+ m_pScreen->addAlias(alias);
+ }
+
+ m_pScreen->setModifier(Screen::Shift, m_pComboBoxShift->currentIndex());
+ m_pScreen->setModifier(Screen::Ctrl, m_pComboBoxCtrl->currentIndex());
+ m_pScreen->setModifier(Screen::Alt, m_pComboBoxAlt->currentIndex());
+ m_pScreen->setModifier(Screen::Meta, m_pComboBoxMeta->currentIndex());
+ m_pScreen->setModifier(Screen::Super, m_pComboBoxSuper->currentIndex());
+
+ m_pScreen->setSwitchCorner(Screen::TopLeft, m_pCheckBoxCornerTopLeft->isChecked());
+ m_pScreen->setSwitchCorner(Screen::TopRight, m_pCheckBoxCornerTopRight->isChecked());
+ m_pScreen->setSwitchCorner(Screen::BottomLeft, m_pCheckBoxCornerBottomLeft->isChecked());
+ m_pScreen->setSwitchCorner(Screen::BottomRight, m_pCheckBoxCornerBottomRight->isChecked());
+ m_pScreen->setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value());
+
+ m_pScreen->setFix(Screen::CapsLock, m_pCheckBoxCapsLock->isChecked());
+ m_pScreen->setFix(Screen::NumLock, m_pCheckBoxNumLock->isChecked());
+ m_pScreen->setFix(Screen::ScrollLock, m_pCheckBoxScrollLock->isChecked());
+ m_pScreen->setFix(Screen::XTest, m_pCheckBoxXTest->isChecked());
+
+ QDialog::accept();
+}
+
+void ScreenSettingsDialog::on_m_pButtonAddAlias_clicked()
+{
+ if (!m_pLineEditAlias->text().isEmpty() && m_pListAliases->findItems(m_pLineEditAlias->text(), Qt::MatchFixedString).isEmpty())
+ {
+ new QListWidgetItem(m_pLineEditAlias->text(), m_pListAliases);
+ m_pLineEditAlias->clear();
+ }
+}
+
+void ScreenSettingsDialog::on_m_pLineEditAlias_textChanged(const QString& text)
+{
+ m_pButtonAddAlias->setEnabled(!text.isEmpty());
+}
+
+void ScreenSettingsDialog::on_m_pButtonRemoveAlias_clicked()
+{
+ QList<QListWidgetItem*> items = m_pListAliases->selectedItems();
+
+ for (int i = 0; i < items.count(); i++)
+ delete items[i];
+}
+
+void ScreenSettingsDialog::on_m_pListAliases_itemSelectionChanged()
+{
+ m_pButtonRemoveAlias->setEnabled(!m_pListAliases->selectedItems().isEmpty());
+}
+
diff --git a/src/gui/src/ScreenSettingsDialog.h b/src/gui/src/ScreenSettingsDialog.h
new file mode 100644
index 0000000..1c525d2
--- /dev/null
+++ b/src/gui/src/ScreenSettingsDialog.h
@@ -0,0 +1,53 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(SCREENSETTINGSDIALOG__H)
+
+#define SCREENSETTINGSDIALOG__H
+
+#include <QDialog>
+
+#include "ui_ScreenSettingsDialogBase.h"
+
+class QWidget;
+class QString;
+
+class Screen;
+
+class ScreenSettingsDialog : public QDialog, public Ui::ScreenSettingsDialogBase
+{
+ Q_OBJECT
+
+ public:
+ ScreenSettingsDialog(QWidget* parent, Screen* pScreen = NULL);
+
+ public slots:
+ void accept();
+
+ private slots:
+ void on_m_pButtonAddAlias_clicked();
+ void on_m_pButtonRemoveAlias_clicked();
+ void on_m_pLineEditAlias_textChanged(const QString& text);
+ void on_m_pListAliases_itemSelectionChanged();
+
+ private:
+ Screen* m_pScreen;
+};
+
+#endif
+
diff --git a/src/gui/src/ScreenSettingsDialogBase.ui b/src/gui/src/ScreenSettingsDialogBase.ui
new file mode 100644
index 0000000..0d6d768
--- /dev/null
+++ b/src/gui/src/ScreenSettingsDialogBase.ui
@@ -0,0 +1,543 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ScreenSettingsDialogBase</class>
+ <widget class="QDialog" name="ScreenSettingsDialogBase">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>434</width>
+ <height>437</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Screen Settings</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>Screen &amp;name:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pLineEditName</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="m_pLineEditName"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QGroupBox" name="m_pGroupAliases">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="title">
+ <string>A&amp;liases</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout">
+ <item row="0" column="0">
+ <widget class="QLineEdit" name="m_pLineEditAlias"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="m_pButtonAddAlias">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Add</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" rowspan="2">
+ <widget class="QListWidget" name="m_pListAliases">
+ <property name="selectionMode">
+ <enum>QAbstractItemView::ExtendedSelection</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="m_pButtonRemoveAlias">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>126</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="m_pGroupModifiers">
+ <property name="title">
+ <string>&amp;Modifier keys</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>&amp;Shift:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pComboBoxShift</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="m_pComboBoxShift">
+ <item>
+ <property name="text">
+ <string>Shift</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Ctrl</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Alt</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Meta</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Super</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>&amp;Ctrl:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pComboBoxCtrl</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="m_pComboBoxCtrl">
+ <property name="currentIndex">
+ <number>1</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Shift</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Ctrl</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Alt</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Meta</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Super</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>Al&amp;t:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pComboBoxAlt</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="m_pComboBoxAlt">
+ <property name="currentIndex">
+ <number>2</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Shift</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Ctrl</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Alt</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Meta</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Super</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>M&amp;eta:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pComboBoxMeta</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QComboBox" name="m_pComboBoxMeta">
+ <property name="currentIndex">
+ <number>3</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Shift</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Ctrl</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Alt</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Meta</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Super</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>S&amp;uper:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pComboBoxSuper</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QComboBox" name="m_pComboBoxSuper">
+ <property name="currentIndex">
+ <number>4</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>Shift</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Ctrl</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Alt</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Meta</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Super</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>None</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QGroupBox" name="m_pGroupSwitchCorners">
+ <property name="title">
+ <string>&amp;Dead corners</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxCornerTopLeft">
+ <property name="text">
+ <string>Top-left</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QCheckBox" name="m_pCheckBoxCornerTopRight">
+ <property name="text">
+ <string>Top-right</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxCornerBottomLeft">
+ <property name="text">
+ <string>Bottom-left</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="m_pCheckBoxCornerBottomRight">
+ <property name="text">
+ <string>Bottom-right</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Corner Si&amp;ze:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pSpinBoxSwitchCornerSize</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="m_pSpinBoxSwitchCornerSize"/>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="m_pGroupFixes">
+ <property name="title">
+ <string>&amp;Fixes</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxCapsLock">
+ <property name="text">
+ <string>Fix CAPS LOCK key</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxNumLock">
+ <property name="text">
+ <string>Fix NUM LOCK key</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxScrollLock">
+ <property name="text">
+ <string>Fix SCROLL LOCK key</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxXTest">
+ <property name="text">
+ <string>Fix XTest for Xinerama</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="m_pButtonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>m_pButtonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ScreenSettingsDialogBase</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>222</x>
+ <y>502</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pButtonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ScreenSettingsDialogBase</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>290</x>
+ <y>508</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/src/ScreenSetupModel.cpp b/src/gui/src/ScreenSetupModel.cpp
new file mode 100644
index 0000000..fce1a8c
--- /dev/null
+++ b/src/gui/src/ScreenSetupModel.cpp
@@ -0,0 +1,143 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "ScreenSetupModel.h"
+#include "Screen.h"
+
+#include <QtCore>
+#include <QtGui>
+
+const QString ScreenSetupModel::m_MimeType = "application/x-qbarrier-screen";
+
+ScreenSetupModel::ScreenSetupModel(ScreenList& screens, int numColumns, int numRows) :
+ QAbstractTableModel(NULL),
+ m_Screens(screens),
+ m_NumColumns(numColumns),
+ m_NumRows(numRows)
+{
+ if (m_NumColumns * m_NumRows > screens.size())
+ qFatal("Not enough elements (%u) in screens QList for %d columns and %d rows", screens.size(), m_NumColumns, m_NumRows);
+}
+
+QVariant ScreenSetupModel::data(const QModelIndex& index, int role) const
+{
+ if (index.isValid() && index.row() < m_NumRows && index.column() < m_NumColumns)
+ {
+ switch(role)
+ {
+ case Qt::DecorationRole:
+ if (screen(index).isNull())
+ break;
+ return QIcon(*screen(index).pixmap());
+
+ case Qt::ToolTipRole:
+ if (screen(index).isNull())
+ break;
+ return QString(tr(
+ "<center>Screen: <b>%1</b></center>"
+ "<br>Double click to edit settings"
+ "<br>Drag screen to the trashcan to remove it")).arg(screen(index).name());
+
+ case Qt::DisplayRole:
+ if (screen(index).isNull())
+ break;
+ return screen(index).name();
+ }
+ }
+
+ return QVariant();
+}
+
+Qt::ItemFlags ScreenSetupModel::flags(const QModelIndex& index) const
+{
+ if (!index.isValid() || index.row() >= m_NumRows || index.column() >= m_NumColumns)
+ return 0;
+
+ if (!screen(index).isNull())
+ return Qt::ItemIsEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
+
+ return Qt::ItemIsDropEnabled;
+}
+
+Qt::DropActions ScreenSetupModel::supportedDropActions() const
+{
+ return Qt::MoveAction | Qt::CopyAction;
+}
+
+QStringList ScreenSetupModel::mimeTypes() const
+{
+ return QStringList() << m_MimeType;
+}
+
+QMimeData* ScreenSetupModel::mimeData(const QModelIndexList& indexes) const
+{
+ QMimeData* pMimeData = new QMimeData();
+ QByteArray encodedData;
+
+ QDataStream stream(&encodedData, QIODevice::WriteOnly);
+
+ foreach (const QModelIndex& index, indexes)
+ if (index.isValid())
+ stream << index.column() << index.row() << screen(index);
+
+ pMimeData->setData(m_MimeType, encodedData);
+
+ return pMimeData;
+}
+
+bool ScreenSetupModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent)
+{
+ if (action == Qt::IgnoreAction)
+ return true;
+
+ if (!data->hasFormat(m_MimeType))
+ return false;
+
+ if (!parent.isValid() || row != -1 || column != -1)
+ return false;
+
+ QByteArray encodedData = data->data(m_MimeType);
+ QDataStream stream(&encodedData, QIODevice::ReadOnly);
+
+ int sourceColumn = -1;
+ int sourceRow = -1;
+
+ stream >> sourceColumn;
+ stream >> sourceRow;
+
+ // don't drop screen onto itself
+ if (sourceColumn == parent.column() && sourceRow == parent.row())
+ return false;
+
+ Screen droppedScreen;
+ stream >> droppedScreen;
+
+ Screen oldScreen = screen(parent.column(), parent.row());
+ if (!oldScreen.isNull() && sourceColumn != -1 && sourceRow != -1)
+ {
+ // mark the screen so it isn't deleted after the dragndrop succeeded
+ // see ScreenSetupView::startDrag()
+ oldScreen.setSwapped(true);
+ screen(sourceColumn, sourceRow) = oldScreen;
+ }
+
+ screen(parent.column(), parent.row()) = droppedScreen;
+
+ return true;
+}
+
diff --git a/src/gui/src/ScreenSetupModel.h b/src/gui/src/ScreenSetupModel.h
new file mode 100644
index 0000000..ba46af3
--- /dev/null
+++ b/src/gui/src/ScreenSetupModel.h
@@ -0,0 +1,71 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(SCREENSETUPMODEL__H)
+
+#define SCREENSETUPMODEL__H
+
+#include <QAbstractTableModel>
+#include <QList>
+#include <QString>
+#include <QStringList>
+
+#include "Screen.h"
+
+class ScreenSetupView;
+class ServerConfigDialog;
+
+class ScreenSetupModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+ friend class ScreenSetupView;
+ friend class ServerConfigDialog;
+
+ public:
+ ScreenSetupModel(ScreenList& screens, int numColumns, int numRows);
+
+ public:
+ static const QString& mimeType() { return m_MimeType; }
+ QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
+ int rowCount() const { return m_NumRows; }
+ int columnCount() const { return m_NumColumns; }
+ int rowCount(const QModelIndex&) const { return rowCount(); }
+ int columnCount(const QModelIndex&) const { return columnCount(); }
+ Qt::DropActions supportedDropActions() const;
+ Qt::ItemFlags flags(const QModelIndex& index) const;
+ QStringList mimeTypes() const;
+ QMimeData* mimeData(const QModelIndexList& indexes) const;
+
+ protected:
+ bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
+ const Screen& screen(const QModelIndex& index) const { return screen(index.column(), index.row()); }
+ Screen& screen(const QModelIndex& index) { return screen(index.column(), index.row()); }
+ const Screen& screen(int column, int row) const { return m_Screens[row * m_NumColumns + column]; }
+ Screen& screen(int column, int row) { return m_Screens[row * m_NumColumns + column]; }
+
+ private:
+ ScreenList& m_Screens;
+ const int m_NumColumns;
+ const int m_NumRows;
+
+ static const QString m_MimeType;
+};
+
+#endif
+
diff --git a/src/gui/src/ScreenSetupView.cpp b/src/gui/src/ScreenSetupView.cpp
new file mode 100644
index 0000000..e42ae17
--- /dev/null
+++ b/src/gui/src/ScreenSetupView.cpp
@@ -0,0 +1,161 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "ScreenSetupView.h"
+#include "ScreenSetupModel.h"
+#include "ScreenSettingsDialog.h"
+
+#include <QtCore>
+#include <QtGui>
+
+ScreenSetupView::ScreenSetupView(QWidget* parent) :
+ QTableView(parent)
+{
+ setDropIndicatorShown(true);
+ setDragDropMode(DragDrop);
+ setSelectionMode(SingleSelection);
+
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+
+ setIconSize(QSize(64, 64));
+ horizontalHeader()->hide();
+ verticalHeader()->hide();
+}
+
+void ScreenSetupView::setModel(QAbstractItemModel* model)
+{
+ QTableView::setModel(model);
+ setTableSize();
+}
+
+ScreenSetupModel* ScreenSetupView::model() const
+{
+ return qobject_cast<ScreenSetupModel*>(QTableView::model());
+}
+
+void ScreenSetupView::setTableSize()
+{
+ for (int i = 0; i < model()->columnCount(); i++)
+ setColumnWidth(i, width() / model()->columnCount());
+
+ for (int i = 0; i < model()->rowCount(); i++)
+ setRowHeight(i, height() / model()->rowCount());
+}
+
+void ScreenSetupView::resizeEvent(QResizeEvent* event)
+{
+ setTableSize();
+ event->ignore();
+}
+
+void ScreenSetupView::mouseDoubleClickEvent(QMouseEvent* event)
+{
+ if (event->buttons() & Qt::LeftButton)
+ {
+ int col = columnAt(event->pos().x());
+ int row = rowAt(event->pos().y());
+
+ if (!model()->screen(col, row).isNull())
+ {
+ ScreenSettingsDialog dlg(this, &model()->screen(col, row));
+ dlg.exec();
+ }
+ }
+ else
+ event->ignore();
+}
+
+void ScreenSetupView::dragEnterEvent(QDragEnterEvent* event)
+{
+ // we accept anything that enters us by a drag as long as the
+ // mime type is okay. anything else is dealt with in dragMoveEvent()
+ if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType()))
+ event->accept();
+ else
+ event->ignore();
+}
+
+void ScreenSetupView::dragMoveEvent(QDragMoveEvent* event)
+{
+ if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType()))
+ {
+ // where does the event come from? myself or someone else?
+ if (event->source() == this)
+ {
+ // myself is ok, but then it must be a move action, never a copy
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ }
+ else
+ {
+ int col = columnAt(event->pos().x());
+ int row = rowAt(event->pos().y());
+
+ // a drop from outside is not allowed if there's a screen already there.
+ if (!model()->screen(col, row).isNull())
+ event->ignore();
+ else
+ event->acceptProposedAction();
+ }
+ }
+ else
+ event->ignore();
+}
+
+// this is reimplemented from QAbstractItemView::startDrag()
+void ScreenSetupView::startDrag(Qt::DropActions)
+{
+ QModelIndexList indexes = selectedIndexes();
+
+ if (indexes.count() != 1)
+ return;
+
+ QMimeData* pData = model()->mimeData(indexes);
+ if (pData == NULL)
+ return;
+
+ QPixmap pixmap = *model()->screen(indexes[0]).pixmap();
+ QDrag* pDrag = new QDrag(this);
+ pDrag->setPixmap(pixmap);
+ pDrag->setMimeData(pData);
+ pDrag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2));
+
+ if (pDrag->exec(Qt::MoveAction, Qt::MoveAction) == Qt::MoveAction)
+ {
+ selectionModel()->clear();
+
+ // make sure to only delete the drag source if screens weren't swapped
+ // see ScreenSetupModel::dropMimeData
+ if (!model()->screen(indexes[0]).swapped())
+ model()->screen(indexes[0]) = Screen();
+ else
+ model()->screen(indexes[0]).setSwapped(false);
+ }
+}
+
+QStyleOptionViewItem ScreenSetupView::viewOptions() const
+{
+ QStyleOptionViewItem option = QTableView::viewOptions();
+ option.showDecorationSelected = true;
+ option.decorationPosition = QStyleOptionViewItem::Top;
+ option.displayAlignment = Qt::AlignCenter;
+ option.textElideMode = Qt::ElideMiddle;
+ return option;
+}
+
diff --git a/src/gui/src/ScreenSetupView.h b/src/gui/src/ScreenSetupView.h
new file mode 100644
index 0000000..a660511
--- /dev/null
+++ b/src/gui/src/ScreenSetupView.h
@@ -0,0 +1,57 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(SCREENSETUPVIEW__H)
+
+#define SCREENSETUPVIEW__H
+
+#include <QTableView>
+#include <QFlags>
+
+#include "Screen.h"
+
+class QWidget;
+class QMouseEvent;
+class QResizeEvent;
+class QDragEnterEvent;
+class ScreenSetupModel;
+
+class ScreenSetupView : public QTableView
+{
+ Q_OBJECT
+
+ public:
+ ScreenSetupView(QWidget* parent);
+
+ public:
+ void setModel(QAbstractItemModel* model) override;
+ ScreenSetupModel* model() const;
+
+ protected:
+ void mouseDoubleClickEvent(QMouseEvent*) override;
+ void setTableSize();
+ void resizeEvent(QResizeEvent*) override;
+ void dragEnterEvent(QDragEnterEvent* event) override;
+ void dragMoveEvent(QDragMoveEvent* event) override;
+ void startDrag(Qt::DropActions supportedActions) override;
+ QStyleOptionViewItem viewOptions() const override;
+ void scrollTo(const QModelIndex&, ScrollHint) override {}
+};
+
+#endif
+
diff --git a/src/gui/src/ServerConfig.cpp b/src/gui/src/ServerConfig.cpp
new file mode 100644
index 0000000..6c19c0e
--- /dev/null
+++ b/src/gui/src/ServerConfig.cpp
@@ -0,0 +1,403 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "ServerConfig.h"
+#include "Hotkey.h"
+#include "MainWindow.h"
+#include "AddClientDialog.h"
+
+#include <QtCore>
+#include <QMessageBox>
+#include <QAbstractButton>
+#include <QPushButton>
+
+static const struct
+{
+ int x;
+ int y;
+ const char* name;
+} neighbourDirs[] =
+{
+ { 1, 0, "right" },
+ { -1, 0, "left" },
+ { 0, -1, "up" },
+ { 0, 1, "down" },
+
+};
+
+const int serverDefaultIndex = 7;
+
+ServerConfig::ServerConfig(QSettings* settings, int numColumns, int numRows ,
+ QString serverName, MainWindow* mainWindow) :
+ m_pSettings(settings),
+ m_Screens(),
+ m_NumColumns(numColumns),
+ m_NumRows(numRows),
+ m_ServerName(serverName),
+ m_IgnoreAutoConfigClient(false),
+ m_EnableDragAndDrop(false),
+ m_ClipboardSharing(true),
+ m_pMainWindow(mainWindow)
+{
+ Q_ASSERT(m_pSettings);
+
+ loadSettings();
+}
+
+ServerConfig::~ServerConfig()
+{
+ saveSettings();
+}
+
+bool ServerConfig::save(const QString& fileName) const
+{
+ QFile file(fileName);
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ return false;
+
+ save(file);
+ file.close();
+
+ return true;
+}
+
+void ServerConfig::save(QFile& file) const
+{
+ QTextStream outStream(&file);
+ outStream << *this;
+}
+
+void ServerConfig::init()
+{
+ switchCorners().clear();
+ screens().clear();
+
+ // m_NumSwitchCorners is used as a fixed size array. See Screen::init()
+ for (int i = 0; i < NumSwitchCorners; i++)
+ switchCorners() << false;
+
+ // There must always be screen objects for each cell in the screens QList. Unused screens
+ // are identified by having an empty name.
+ for (int i = 0; i < numColumns() * numRows(); i++)
+ addScreen(Screen());
+}
+
+void ServerConfig::saveSettings()
+{
+ settings().beginGroup("internalConfig");
+ settings().remove("");
+
+ settings().setValue("numColumns", numColumns());
+ settings().setValue("numRows", numRows());
+
+ settings().setValue("hasHeartbeat", hasHeartbeat());
+ settings().setValue("heartbeat", heartbeat());
+ settings().setValue("relativeMouseMoves", relativeMouseMoves());
+ settings().setValue("screenSaverSync", screenSaverSync());
+ settings().setValue("win32KeepForeground", win32KeepForeground());
+ settings().setValue("hasSwitchDelay", hasSwitchDelay());
+ settings().setValue("switchDelay", switchDelay());
+ settings().setValue("hasSwitchDoubleTap", hasSwitchDoubleTap());
+ settings().setValue("switchDoubleTap", switchDoubleTap());
+ settings().setValue("switchCornerSize", switchCornerSize());
+ settings().setValue("ignoreAutoConfigClient", ignoreAutoConfigClient());
+ settings().setValue("enableDragAndDrop", enableDragAndDrop());
+
+ writeSettings(settings(), switchCorners(), "switchCorner");
+
+ settings().beginWriteArray("screens");
+ for (int i = 0; i < screens().size(); i++)
+ {
+ settings().setArrayIndex(i);
+ screens()[i].saveSettings(settings());
+ }
+ settings().endArray();
+
+ settings().beginWriteArray("hotkeys");
+ for (int i = 0; i < hotkeys().size(); i++)
+ {
+ settings().setArrayIndex(i);
+ hotkeys()[i].saveSettings(settings());
+ }
+ settings().endArray();
+
+ settings().endGroup();
+}
+
+void ServerConfig::loadSettings()
+{
+ settings().beginGroup("internalConfig");
+
+ setNumColumns(settings().value("numColumns", 5).toInt());
+ setNumRows(settings().value("numRows", 3).toInt());
+
+ // we need to know the number of columns and rows before we can set up ourselves
+ init();
+
+ haveHeartbeat(settings().value("hasHeartbeat", false).toBool());
+ setHeartbeat(settings().value("heartbeat", 5000).toInt());
+ setRelativeMouseMoves(settings().value("relativeMouseMoves", false).toBool());
+ setScreenSaverSync(settings().value("screenSaverSync", true).toBool());
+ setWin32KeepForeground(settings().value("win32KeepForeground", false).toBool());
+ haveSwitchDelay(settings().value("hasSwitchDelay", false).toBool());
+ setSwitchDelay(settings().value("switchDelay", 250).toInt());
+ haveSwitchDoubleTap(settings().value("hasSwitchDoubleTap", false).toBool());
+ setSwitchDoubleTap(settings().value("switchDoubleTap", 250).toInt());
+ setSwitchCornerSize(settings().value("switchCornerSize").toInt());
+ setIgnoreAutoConfigClient(settings().value("ignoreAutoConfigClient").toBool());
+ setEnableDragAndDrop(settings().value("enableDragAndDrop", true).toBool());
+
+ readSettings(settings(), switchCorners(), "switchCorner", false, NumSwitchCorners);
+
+ int numScreens = settings().beginReadArray("screens");
+ Q_ASSERT(numScreens <= screens().size());
+ for (int i = 0; i < numScreens; i++)
+ {
+ settings().setArrayIndex(i);
+ screens()[i].loadSettings(settings());
+ }
+ settings().endArray();
+
+ int numHotkeys = settings().beginReadArray("hotkeys");
+ for (int i = 0; i < numHotkeys; i++)
+ {
+ settings().setArrayIndex(i);
+ Hotkey h;
+ h.loadSettings(settings());
+ hotkeys().append(h);
+ }
+ settings().endArray();
+
+ settings().endGroup();
+}
+
+int ServerConfig::adjacentScreenIndex(int idx, int deltaColumn, int deltaRow) const
+{
+ if (screens()[idx].isNull())
+ return -1;
+
+ // if we're at the left or right end of the table, don't find results going further left or right
+ if ((deltaColumn > 0 && (idx+1) % numColumns() == 0)
+ || (deltaColumn < 0 && idx % numColumns() == 0))
+ return -1;
+
+ int arrayPos = idx + deltaColumn + deltaRow * numColumns();
+
+ if (arrayPos >= screens().size() || arrayPos < 0)
+ return -1;
+
+ return arrayPos;
+}
+
+QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config)
+{
+ outStream << "section: screens" << endl;
+
+ foreach (const Screen& s, config.screens())
+ if (!s.isNull())
+ s.writeScreensSection(outStream);
+
+ outStream << "end" << endl << endl;
+
+ outStream << "section: aliases" << endl;
+
+ foreach (const Screen& s, config.screens())
+ if (!s.isNull())
+ s.writeAliasesSection(outStream);
+
+ outStream << "end" << endl << endl;
+
+ outStream << "section: links" << endl;
+
+ for (int i = 0; i < config.screens().size(); i++)
+ if (!config.screens()[i].isNull())
+ {
+ outStream << "\t" << config.screens()[i].name() << ":" << endl;
+
+ for (unsigned int j = 0; j < sizeof(neighbourDirs) / sizeof(neighbourDirs[0]); j++)
+ {
+ int idx = config.adjacentScreenIndex(i, neighbourDirs[j].x, neighbourDirs[j].y);
+ if (idx != -1 && !config.screens()[idx].isNull())
+ outStream << "\t\t" << neighbourDirs[j].name << " = " << config.screens()[idx].name() << endl;
+ }
+ }
+
+ outStream << "end" << endl << endl;
+
+ outStream << "section: options" << endl;
+
+ if (config.hasHeartbeat())
+ outStream << "\t" << "heartbeat = " << config.heartbeat() << endl;
+
+ outStream << "\t" << "relativeMouseMoves = " << (config.relativeMouseMoves() ? "true" : "false") << endl;
+ outStream << "\t" << "screenSaverSync = " << (config.screenSaverSync() ? "true" : "false") << endl;
+ outStream << "\t" << "win32KeepForeground = " << (config.win32KeepForeground() ? "true" : "false") << endl;
+ outStream << "\t" << "clipboardSharing = " << (config.clipboardSharing() ? "true" : "false") << endl;
+
+ if (config.hasSwitchDelay())
+ outStream << "\t" << "switchDelay = " << config.switchDelay() << endl;
+
+ if (config.hasSwitchDoubleTap())
+ outStream << "\t" << "switchDoubleTap = " << config.switchDoubleTap() << endl;
+
+ outStream << "\t" << "switchCorners = none ";
+ for (int i = 0; i < config.switchCorners().size(); i++)
+ if (config.switchCorners()[i])
+ outStream << "+" << config.switchCornerName(i) << " ";
+ outStream << endl;
+
+ outStream << "\t" << "switchCornerSize = " << config.switchCornerSize() << endl;
+
+ foreach(const Hotkey& hotkey, config.hotkeys())
+ outStream << hotkey;
+
+ outStream << "end" << endl << endl;
+
+ return outStream;
+}
+
+int ServerConfig::numScreens() const
+{
+ int rval = 0;
+
+ foreach(const Screen& s, screens())
+ if (!s.isNull())
+ rval++;
+
+ return rval;
+}
+
+int ServerConfig::autoAddScreen(const QString name)
+{
+ int serverIndex = -1;
+ int targetIndex = -1;
+ if (!findScreenName(m_ServerName, serverIndex)) {
+ if (!fixNoServer(m_ServerName, serverIndex)) {
+ return kAutoAddScreenManualServer;
+ }
+ }
+ if (findScreenName(name, targetIndex)) {
+ // already exists.
+ return kAutoAddScreenIgnore;
+ }
+
+ int result = showAddClientDialog(name);
+
+ if (result == kAddClientIgnore) {
+ return kAutoAddScreenIgnore;
+ }
+
+ if (result == kAddClientOther) {
+ addToFirstEmptyGrid(name);
+ return kAutoAddScreenManualClient;
+ }
+
+ bool success = false;
+ int startIndex = serverIndex;
+ int offset = 1;
+ int dirIndex = 0;
+
+ if (result == kAddClientLeft) {
+ offset = -1;
+ dirIndex = 1;
+ }
+ else if (result == kAddClientUp) {
+ offset = -5;
+ dirIndex = 2;
+ }
+ else if (result == kAddClientDown) {
+ offset = 5;
+ dirIndex = 3;
+ }
+
+
+ int idx = adjacentScreenIndex(startIndex, neighbourDirs[dirIndex].x,
+ neighbourDirs[dirIndex].y);
+ while (idx != -1) {
+ if (screens()[idx].isNull()) {
+ m_Screens[idx].setName(name);
+ success = true;
+ break;
+ }
+
+ startIndex += offset;
+ idx = adjacentScreenIndex(startIndex, neighbourDirs[dirIndex].x,
+ neighbourDirs[dirIndex].y);
+ }
+
+ if (!success) {
+ addToFirstEmptyGrid(name);
+ return kAutoAddScreenManualClient;
+ }
+
+ saveSettings();
+ return kAutoAddScreenOk;
+}
+
+bool ServerConfig::findScreenName(const QString& name, int& index)
+{
+ bool found = false;
+ for (int i = 0; i < screens().size(); i++) {
+ if (!screens()[i].isNull() &&
+ screens()[i].name().compare(name) == 0) {
+ index = i;
+ found = true;
+ break;
+ }
+ }
+ return found;
+}
+
+bool ServerConfig::fixNoServer(const QString& name, int& index)
+{
+ bool fixed = false;
+ if (screens()[serverDefaultIndex].isNull()) {
+ m_Screens[serverDefaultIndex].setName(name);
+ index = serverDefaultIndex;
+ fixed = true;
+ }
+
+ return fixed;
+}
+
+int ServerConfig::showAddClientDialog(const QString& clientName)
+{
+ int result = kAddClientIgnore;
+
+ if (!m_pMainWindow->isActiveWindow()) {
+ m_pMainWindow->showNormal();
+ m_pMainWindow->activateWindow();
+ }
+
+ AddClientDialog addClientDialog(clientName, m_pMainWindow);
+ addClientDialog.exec();
+ result = addClientDialog.addResult();
+ m_IgnoreAutoConfigClient = addClientDialog.ignoreAutoConfigClient();
+
+ return result;
+}
+
+void::ServerConfig::addToFirstEmptyGrid(const QString &clientName)
+{
+ for (int i = 0; i < screens().size(); i++) {
+ if (screens()[i].isNull()) {
+ m_Screens[i].setName(clientName);
+ break;
+ }
+ }
+}
diff --git a/src/gui/src/ServerConfig.h b/src/gui/src/ServerConfig.h
new file mode 100644
index 0000000..65cbbac
--- /dev/null
+++ b/src/gui/src/ServerConfig.h
@@ -0,0 +1,141 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(SERVERCONFIG__H)
+
+#define SERVERCONFIG__H
+
+#include <QList>
+
+#include "Screen.h"
+#include "BaseConfig.h"
+#include "Hotkey.h"
+
+class QTextStream;
+class QSettings;
+class QString;
+class QFile;
+class ServerConfigDialog;
+class MainWindow;
+
+class ServerConfig : public BaseConfig
+{
+ friend class ServerConfigDialog;
+ friend QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config);
+
+ public:
+ ServerConfig(QSettings* settings, int numColumns, int numRows,
+ QString serverName, MainWindow* mainWindow);
+ ~ServerConfig();
+
+ public:
+ const ScreenList& screens() const { return m_Screens; }
+ int numColumns() const { return m_NumColumns; }
+ int numRows() const { return m_NumRows; }
+ bool hasHeartbeat() const { return m_HasHeartbeat; }
+ int heartbeat() const { return m_Heartbeat; }
+ bool relativeMouseMoves() const { return m_RelativeMouseMoves; }
+ bool screenSaverSync() const { return m_ScreenSaverSync; }
+ bool win32KeepForeground() const { return m_Win32KeepForeground; }
+ bool hasSwitchDelay() const { return m_HasSwitchDelay; }
+ int switchDelay() const { return m_SwitchDelay; }
+ bool hasSwitchDoubleTap() const { return m_HasSwitchDoubleTap; }
+ int switchDoubleTap() const { return m_SwitchDoubleTap; }
+ bool switchCorner(int c) const { return m_SwitchCorners[c]; }
+ int switchCornerSize() const { return m_SwitchCornerSize; }
+ const QList<bool>& switchCorners() const { return m_SwitchCorners; }
+ const HotkeyList& hotkeys() const { return m_Hotkeys; }
+ bool ignoreAutoConfigClient() const { return m_IgnoreAutoConfigClient; }
+ bool enableDragAndDrop() const { return m_EnableDragAndDrop; }
+ bool clipboardSharing() const { return m_ClipboardSharing; }
+
+ void saveSettings();
+ void loadSettings();
+ bool save(const QString& fileName) const;
+ void save(QFile& file) const;
+ int numScreens() const;
+ int autoAddScreen(const QString name);
+
+ protected:
+ QSettings& settings() { return *m_pSettings; }
+ ScreenList& screens() { return m_Screens; }
+ void setScreens(const ScreenList& screens) { m_Screens = screens; }
+ void addScreen(const Screen& screen) { m_Screens.append(screen); }
+ void setNumColumns(int n) { m_NumColumns = n; }
+ void setNumRows(int n) { m_NumRows = n; }
+ void haveHeartbeat(bool on) { m_HasHeartbeat = on; }
+ void setHeartbeat(int val) { m_Heartbeat = val; }
+ void setRelativeMouseMoves(bool on) { m_RelativeMouseMoves = on; }
+ void setScreenSaverSync(bool on) { m_ScreenSaverSync = on; }
+ void setWin32KeepForeground(bool on) { m_Win32KeepForeground = on; }
+ void haveSwitchDelay(bool on) { m_HasSwitchDelay = on; }
+ void setSwitchDelay(int val) { m_SwitchDelay = val; }
+ void haveSwitchDoubleTap(bool on) { m_HasSwitchDoubleTap = on; }
+ void setSwitchDoubleTap(int val) { m_SwitchDoubleTap = val; }
+ void setSwitchCorner(int c, bool on) { m_SwitchCorners[c] = on; }
+ void setSwitchCornerSize(int val) { m_SwitchCornerSize = val; }
+ void setIgnoreAutoConfigClient(bool on) { m_IgnoreAutoConfigClient = on; }
+ void setEnableDragAndDrop(bool on) { m_EnableDragAndDrop = on; }
+ void setClipboardSharing(bool on) { m_ClipboardSharing = on; }
+ QList<bool>& switchCorners() { return m_SwitchCorners; }
+ HotkeyList& hotkeys() { return m_Hotkeys; }
+
+ void init();
+ int adjacentScreenIndex(int idx, int deltaColumn, int deltaRow) const;
+
+ private:
+ bool findScreenName(const QString& name, int& index);
+ bool fixNoServer(const QString& name, int& index);
+ int showAddClientDialog(const QString& clientName);
+ void addToFirstEmptyGrid(const QString& clientName);
+
+ private:
+ QSettings* m_pSettings;
+ ScreenList m_Screens;
+ int m_NumColumns;
+ int m_NumRows;
+ bool m_HasHeartbeat;
+ int m_Heartbeat;
+ bool m_RelativeMouseMoves;
+ bool m_ScreenSaverSync;
+ bool m_Win32KeepForeground;
+ bool m_HasSwitchDelay;
+ int m_SwitchDelay;
+ bool m_HasSwitchDoubleTap;
+ int m_SwitchDoubleTap;
+ int m_SwitchCornerSize;
+ QList<bool> m_SwitchCorners;
+ HotkeyList m_Hotkeys;
+ QString m_ServerName;
+ bool m_IgnoreAutoConfigClient;
+ bool m_EnableDragAndDrop;
+ bool m_ClipboardSharing;
+ MainWindow* m_pMainWindow;
+};
+
+QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config);
+
+enum {
+ kAutoAddScreenOk,
+ kAutoAddScreenManualServer,
+ kAutoAddScreenManualClient,
+ kAutoAddScreenIgnore
+};
+
+#endif
+
diff --git a/src/gui/src/ServerConfigDialog.cpp b/src/gui/src/ServerConfigDialog.cpp
new file mode 100644
index 0000000..799cfc8
--- /dev/null
+++ b/src/gui/src/ServerConfigDialog.cpp
@@ -0,0 +1,219 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "ServerConfigDialog.h"
+#include "ServerConfig.h"
+#include "HotkeyDialog.h"
+#include "ActionDialog.h"
+
+#include <QtCore>
+#include <QtGui>
+#include <QMessageBox>
+
+ServerConfigDialog::ServerConfigDialog(QWidget* parent, ServerConfig& config, const QString& defaultScreenName) :
+ QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
+ Ui::ServerConfigDialogBase(),
+ m_OrigServerConfig(config),
+ m_ServerConfig(config),
+ m_ScreenSetupModel(serverConfig().screens(), serverConfig().numColumns(), serverConfig().numRows()),
+ m_Message("")
+{
+ setupUi(this);
+
+ m_pCheckBoxHeartbeat->setChecked(serverConfig().hasHeartbeat());
+ m_pSpinBoxHeartbeat->setValue(serverConfig().heartbeat());
+
+ m_pCheckBoxRelativeMouseMoves->setChecked(serverConfig().relativeMouseMoves());
+ m_pCheckBoxScreenSaverSync->setChecked(serverConfig().screenSaverSync());
+ m_pCheckBoxWin32KeepForeground->setChecked(serverConfig().win32KeepForeground());
+
+ m_pCheckBoxSwitchDelay->setChecked(serverConfig().hasSwitchDelay());
+ m_pSpinBoxSwitchDelay->setValue(serverConfig().switchDelay());
+
+ m_pCheckBoxSwitchDoubleTap->setChecked(serverConfig().hasSwitchDoubleTap());
+ m_pSpinBoxSwitchDoubleTap->setValue(serverConfig().switchDoubleTap());
+
+ m_pCheckBoxCornerTopLeft->setChecked(serverConfig().switchCorner(BaseConfig::TopLeft));
+ m_pCheckBoxCornerTopRight->setChecked(serverConfig().switchCorner(BaseConfig::TopRight));
+ m_pCheckBoxCornerBottomLeft->setChecked(serverConfig().switchCorner(BaseConfig::BottomLeft));
+ m_pCheckBoxCornerBottomRight->setChecked(serverConfig().switchCorner(BaseConfig::BottomRight));
+ m_pSpinBoxSwitchCornerSize->setValue(serverConfig().switchCornerSize());
+
+ m_pCheckBoxIgnoreAutoConfigClient->setChecked(serverConfig().ignoreAutoConfigClient());
+
+ m_pCheckBoxEnableDragAndDrop->setChecked(serverConfig().enableDragAndDrop());
+
+ m_pCheckBoxEnableClipboard->setChecked(serverConfig().clipboardSharing());
+
+ foreach(const Hotkey& hotkey, serverConfig().hotkeys())
+ m_pListHotkeys->addItem(hotkey.text());
+
+ m_pScreenSetupView->setModel(&m_ScreenSetupModel);
+
+ if (serverConfig().numScreens() == 0)
+ model().screen(serverConfig().numColumns() / 2, serverConfig().numRows() / 2) = Screen(defaultScreenName);
+}
+
+void ServerConfigDialog::showEvent(QShowEvent* event)
+{
+ QDialog::show();
+
+ if (!m_Message.isEmpty())
+ {
+ // TODO: ideally this massage box should pop up after the dialog is shown
+ QMessageBox::information(this, tr("Configure server"), m_Message);
+ }
+}
+
+void ServerConfigDialog::accept()
+{
+ serverConfig().haveHeartbeat(m_pCheckBoxHeartbeat->isChecked());
+ serverConfig().setHeartbeat(m_pSpinBoxHeartbeat->value());
+
+ serverConfig().setRelativeMouseMoves(m_pCheckBoxRelativeMouseMoves->isChecked());
+ serverConfig().setScreenSaverSync(m_pCheckBoxScreenSaverSync->isChecked());
+ serverConfig().setWin32KeepForeground(m_pCheckBoxWin32KeepForeground->isChecked());
+
+ serverConfig().haveSwitchDelay(m_pCheckBoxSwitchDelay->isChecked());
+ serverConfig().setSwitchDelay(m_pSpinBoxSwitchDelay->value());
+
+ serverConfig().haveSwitchDoubleTap(m_pCheckBoxSwitchDoubleTap->isChecked());
+ serverConfig().setSwitchDoubleTap(m_pSpinBoxSwitchDoubleTap->value());
+
+ serverConfig().setSwitchCorner(BaseConfig::TopLeft, m_pCheckBoxCornerTopLeft->isChecked());
+ serverConfig().setSwitchCorner(BaseConfig::TopRight, m_pCheckBoxCornerTopRight->isChecked());
+ serverConfig().setSwitchCorner(BaseConfig::BottomLeft, m_pCheckBoxCornerBottomLeft->isChecked());
+ serverConfig().setSwitchCorner(BaseConfig::BottomRight, m_pCheckBoxCornerBottomRight->isChecked());
+ serverConfig().setSwitchCornerSize(m_pSpinBoxSwitchCornerSize->value());
+ serverConfig().setIgnoreAutoConfigClient(m_pCheckBoxIgnoreAutoConfigClient->isChecked());
+ serverConfig().setEnableDragAndDrop(m_pCheckBoxEnableDragAndDrop->isChecked());
+ serverConfig().setClipboardSharing(m_pCheckBoxEnableClipboard->isChecked());
+
+ // now that the dialog has been accepted, copy the new server config to the original one,
+ // which is a reference to the one in MainWindow.
+ setOrigServerConfig(serverConfig());
+
+ QDialog::accept();
+}
+
+void ServerConfigDialog::on_m_pButtonNewHotkey_clicked()
+{
+ Hotkey hotkey;
+ HotkeyDialog dlg(this, hotkey);
+ if (dlg.exec() == QDialog::Accepted)
+ {
+ serverConfig().hotkeys().append(hotkey);
+ m_pListHotkeys->addItem(hotkey.text());
+ }
+}
+
+void ServerConfigDialog::on_m_pButtonEditHotkey_clicked()
+{
+ int idx = m_pListHotkeys->currentRow();
+ Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size());
+ Hotkey& hotkey = serverConfig().hotkeys()[idx];
+ HotkeyDialog dlg(this, hotkey);
+ if (dlg.exec() == QDialog::Accepted)
+ m_pListHotkeys->currentItem()->setText(hotkey.text());
+}
+
+void ServerConfigDialog::on_m_pButtonRemoveHotkey_clicked()
+{
+ int idx = m_pListHotkeys->currentRow();
+ Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size());
+ serverConfig().hotkeys().removeAt(idx);
+ m_pListActions->clear();
+ delete m_pListHotkeys->item(idx);
+}
+
+void ServerConfigDialog::on_m_pListHotkeys_itemSelectionChanged()
+{
+ bool itemsSelected = !m_pListHotkeys->selectedItems().isEmpty();
+ m_pButtonEditHotkey->setEnabled(itemsSelected);
+ m_pButtonRemoveHotkey->setEnabled(itemsSelected);
+ m_pButtonNewAction->setEnabled(itemsSelected);
+
+ if (itemsSelected && serverConfig().hotkeys().size() > 0)
+ {
+ m_pListActions->clear();
+
+ int idx = m_pListHotkeys->row(m_pListHotkeys->selectedItems()[0]);
+
+ // There's a bug somewhere around here: We get idx == 1 right after we deleted the next to last item, so idx can
+ // only possibly be 0. GDB shows we got called indirectly from the delete line in
+ // on_m_pButtonRemoveHotkey_clicked() above, but the delete is of course necessary and seems correct.
+ // The while() is a generalized workaround for all that and shouldn't be required.
+ while (idx >= 0 && idx >= serverConfig().hotkeys().size())
+ idx--;
+
+ Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size());
+
+ const Hotkey& hotkey = serverConfig().hotkeys()[idx];
+ foreach(const Action& action, hotkey.actions())
+ m_pListActions->addItem(action.text());
+ }
+}
+
+void ServerConfigDialog::on_m_pButtonNewAction_clicked()
+{
+ int idx = m_pListHotkeys->currentRow();
+ Q_ASSERT(idx >= 0 && idx < serverConfig().hotkeys().size());
+ Hotkey& hotkey = serverConfig().hotkeys()[idx];
+
+ Action action;
+ ActionDialog dlg(this, serverConfig(), hotkey, action);
+ if (dlg.exec() == QDialog::Accepted)
+ {
+ hotkey.actions().append(action);
+ m_pListActions->addItem(action.text());
+ }
+}
+
+void ServerConfigDialog::on_m_pButtonEditAction_clicked()
+{
+ int idxHotkey = m_pListHotkeys->currentRow();
+ Q_ASSERT(idxHotkey >= 0 && idxHotkey < serverConfig().hotkeys().size());
+ Hotkey& hotkey = serverConfig().hotkeys()[idxHotkey];
+
+ int idxAction = m_pListActions->currentRow();
+ Q_ASSERT(idxAction >= 0 && idxAction < hotkey.actions().size());
+ Action& action = hotkey.actions()[idxAction];
+
+ ActionDialog dlg(this, serverConfig(), hotkey, action);
+ if (dlg.exec() == QDialog::Accepted)
+ m_pListActions->currentItem()->setText(action.text());
+}
+
+void ServerConfigDialog::on_m_pButtonRemoveAction_clicked()
+{
+ int idxHotkey = m_pListHotkeys->currentRow();
+ Q_ASSERT(idxHotkey >= 0 && idxHotkey < serverConfig().hotkeys().size());
+ Hotkey& hotkey = serverConfig().hotkeys()[idxHotkey];
+
+ int idxAction = m_pListActions->currentRow();
+ Q_ASSERT(idxAction >= 0 && idxAction < hotkey.actions().size());
+
+ hotkey.actions().removeAt(idxAction);
+ delete m_pListActions->currentItem();
+}
+
+void ServerConfigDialog::on_m_pListActions_itemSelectionChanged()
+{
+ m_pButtonEditAction->setEnabled(!m_pListActions->selectedItems().isEmpty());
+ m_pButtonRemoveAction->setEnabled(!m_pListActions->selectedItems().isEmpty());
+}
diff --git a/src/gui/src/ServerConfigDialog.h b/src/gui/src/ServerConfigDialog.h
new file mode 100644
index 0000000..1ef17d1
--- /dev/null
+++ b/src/gui/src/ServerConfigDialog.h
@@ -0,0 +1,66 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(SERVERCONFIGDIALOG__H)
+
+#define SERVERCONFIGDIALOG__H
+
+#include "ScreenSetupModel.h"
+#include "ServerConfig.h"
+
+#include "ui_ServerConfigDialogBase.h"
+
+#include <QDialog>
+
+class ServerConfigDialog : public QDialog, public Ui::ServerConfigDialogBase
+{
+ Q_OBJECT
+
+ public:
+ ServerConfigDialog(QWidget* parent, ServerConfig& config, const QString& defaultScreenName);
+
+ public slots:
+ void accept();
+ void showEvent(QShowEvent* event);
+ void message(const QString& message) { m_Message = message; }
+
+ protected slots:
+ void on_m_pButtonNewHotkey_clicked();
+ void on_m_pListHotkeys_itemSelectionChanged();
+ void on_m_pButtonEditHotkey_clicked();
+ void on_m_pButtonRemoveHotkey_clicked();
+
+ void on_m_pButtonNewAction_clicked();
+ void on_m_pListActions_itemSelectionChanged();
+ void on_m_pButtonEditAction_clicked();
+ void on_m_pButtonRemoveAction_clicked();
+
+ protected:
+ ServerConfig& serverConfig() { return m_ServerConfig; }
+ void setOrigServerConfig(const ServerConfig& s) { m_OrigServerConfig = s; }
+ ScreenSetupModel& model() { return m_ScreenSetupModel; }
+
+ private:
+ ServerConfig& m_OrigServerConfig;
+ ServerConfig m_ServerConfig;
+ ScreenSetupModel m_ScreenSetupModel;
+ QString m_Message;
+};
+
+#endif
+
diff --git a/src/gui/src/ServerConfigDialogBase.ui b/src/gui/src/ServerConfigDialogBase.ui
new file mode 100644
index 0000000..c360420
--- /dev/null
+++ b/src/gui/src/ServerConfigDialogBase.ui
@@ -0,0 +1,781 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ServerConfigDialogBase</class>
+ <widget class="QDialog" name="ServerConfigDialogBase">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>795</width>
+ <height>534</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Server Configuration</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QTabWidget" name="m_pTabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="m_pTabScreens">
+ <attribute name="title">
+ <string>Screens and links</string>
+ </attribute>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="TrashScreenWidget" name="m_pTrashScreenWidget">
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip">
+ <string>Drag a screen from the grid to the trashcan to remove it.</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="Barrier.qrc">:/res/icons/64x64/user-trash.png</pixmap>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Configure the layout of your barrier server configuration.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="NewScreenWidget" name="m_pLabelNewScreenWidget">
+ <property name="toolTip">
+ <string>Drag this button to the grid to add a new screen.</string>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ <property name="pixmap">
+ <pixmap resource="Barrier.qrc">:/res/icons/64x64/video-display.png</pixmap>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="ScreenSetupView" name="m_pScreenSetupView">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>273</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>273</height>
+ </size>
+ </property>
+ <property name="acceptDrops">
+ <bool>true</bool>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>1</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Drag new screens to the grid or move existing ones around.
+Drag a screen to the trashcan to delete it.
+Double click on a screen to edit its settings.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="m_pTabHotkeys">
+ <attribute name="title">
+ <string>Hotkeys</string>
+ </attribute>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>&amp;Hotkeys</string>
+ </property>
+ <layout class="QGridLayout">
+ <item row="0" column="0" rowspan="4">
+ <widget class="QListWidget" name="m_pListHotkeys"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="m_pButtonNewHotkey">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;New</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="m_pButtonEditHotkey">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Edit</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QPushButton" name="m_pButtonRemoveHotkey">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Remove</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>75</width>
+ <height>161</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2">
+ <property name="title">
+ <string>A&amp;ctions</string>
+ </property>
+ <layout class="QGridLayout">
+ <item row="0" column="0" rowspan="4">
+ <widget class="QListWidget" name="m_pListActions"/>
+ </item>
+ <item row="0" column="1">
+ <widget class="QPushButton" name="m_pButtonNewAction">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Ne&amp;w</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QPushButton" name="m_pButtonEditAction">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>E&amp;dit</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QPushButton" name="m_pButtonRemoveAction">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Re&amp;move</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="m_pTabAdvanced">
+ <attribute name="title">
+ <string>Advanced server settings</string>
+ </attribute>
+ <layout class="QGridLayout">
+ <item row="0" column="0">
+ <widget class="QGroupBox" name="m_pGroupSwitch">
+ <property name="title">
+ <string>&amp;Switch</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QCheckBox" name="m_pCheckBoxSwitchDelay">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Switch &amp;after waiting</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="m_pSpinBoxSwitchDelay">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimum">
+ <number>10</number>
+ </property>
+ <property name="maximum">
+ <number>10000</number>
+ </property>
+ <property name="singleStep">
+ <number>10</number>
+ </property>
+ <property name="value">
+ <number>250</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_pLabel_14">
+ <property name="text">
+ <string>ms</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QCheckBox" name="m_pCheckBoxSwitchDoubleTap">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Switch on double &amp;tap within</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="m_pSpinBoxSwitchDoubleTap">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimum">
+ <number>10</number>
+ </property>
+ <property name="maximum">
+ <number>10000</number>
+ </property>
+ <property name="singleStep">
+ <number>10</number>
+ </property>
+ <property name="value">
+ <number>250</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_pLabel_15">
+ <property name="text">
+ <string>ms</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QGroupBox" name="m_pGroupOptions">
+ <property name="title">
+ <string>&amp;Options</string>
+ </property>
+ <layout class="QGridLayout">
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxWin32KeepForeground">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Don't take &amp;foreground window on Windows servers</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxRelativeMouseMoves">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Use &amp;relative mouse moves</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxScreenSaverSync">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>S&amp;ynchronize screen savers</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QCheckBox" name="m_pCheckBoxHeartbeat">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>&amp;Check clients every</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="m_pSpinBoxHeartbeat">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimum">
+ <number>1000</number>
+ </property>
+ <property name="maximum">
+ <number>30000</number>
+ </property>
+ <property name="singleStep">
+ <number>1000</number>
+ </property>
+ <property name="value">
+ <number>5000</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_pLabel_16">
+ <property name="text">
+ <string>ms</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="7" column="0">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>16</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="5" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxEnableDragAndDrop">
+ <property name="text">
+ <string>Enable drag and drop file transfers</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxIgnoreAutoConfigClient">
+ <property name="text">
+ <string>Ignore auto config clients</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxEnableClipboard">
+ <property name="text">
+ <string>Enable clipboard sharing</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QGroupBox" name="m_pGroupSwitchCorners">
+ <property name="title">
+ <string>&amp;Dead corners</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout">
+ <item row="0" column="0" colspan="2">
+ <widget class="QCheckBox" name="m_pCheckBoxCornerTopLeft">
+ <property name="text">
+ <string>To&amp;p-left</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" colspan="2">
+ <widget class="QCheckBox" name="m_pCheckBoxCornerTopRight">
+ <property name="text">
+ <string>Top-rig&amp;ht</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" colspan="2">
+ <widget class="QCheckBox" name="m_pCheckBoxCornerBottomLeft">
+ <property name="text">
+ <string>&amp;Bottom-left</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" colspan="2">
+ <widget class="QCheckBox" name="m_pCheckBoxCornerBottomRight">
+ <property name="text">
+ <string>Bottom-ri&amp;ght</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="1" colspan="2">
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Cor&amp;ner Size:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pSpinBoxSwitchCornerSize</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QSpinBox" name="m_pSpinBoxSwitchCornerSize"/>
+ </item>
+ </layout>
+ </item>
+ <item row="2" column="3">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="m_pButtonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>ScreenSetupView</class>
+ <extends>QTableView</extends>
+ <header>ScreenSetupView.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
+ <class>NewScreenWidget</class>
+ <extends>QLabel</extends>
+ <header>NewScreenWidget.h</header>
+ </customwidget>
+ <customwidget>
+ <class>TrashScreenWidget</class>
+ <extends>QLabel</extends>
+ <header>TrashScreenWidget.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="Barrier.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>m_pButtonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ServerConfigDialogBase</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>572</x>
+ <y>424</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>377</x>
+ <y>-8</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pButtonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ServerConfigDialogBase</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>641</x>
+ <y>424</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>595</x>
+ <y>1</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pCheckBoxSwitchDelay</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pSpinBoxSwitchDelay</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>110</x>
+ <y>63</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>110</x>
+ <y>63</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pCheckBoxSwitchDoubleTap</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pSpinBoxSwitchDoubleTap</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>110</x>
+ <y>63</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>110</x>
+ <y>63</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pCheckBoxHeartbeat</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>m_pSpinBoxHeartbeat</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>110</x>
+ <y>63</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>110</x>
+ <y>63</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pListHotkeys</sender>
+ <signal>itemDoubleClicked(QListWidgetItem*)</signal>
+ <receiver>m_pButtonEditHotkey</receiver>
+ <slot>click()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>197</x>
+ <y>115</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>304</x>
+ <y>115</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>m_pListActions</sender>
+ <signal>itemDoubleClicked(QListWidgetItem*)</signal>
+ <receiver>m_pButtonEditAction</receiver>
+ <slot>click()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>505</x>
+ <y>120</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>677</x>
+ <y>118</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/src/SettingsDialog.cpp b/src/gui/src/SettingsDialog.cpp
new file mode 100644
index 0000000..dc07313
--- /dev/null
+++ b/src/gui/src/SettingsDialog.cpp
@@ -0,0 +1,140 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "SettingsDialog.h"
+
+#include "CoreInterface.h"
+#include "BarrierLocale.h"
+#include "QBarrierApplication.h"
+#include "QUtility.h"
+#include "AppConfig.h"
+#include "SslCertificate.h"
+#include "MainWindow.h"
+
+#include <QtCore>
+#include <QtGui>
+#include <QMessageBox>
+#include <QFileDialog>
+#include <QDir>
+
+static const char networkSecurity[] = "ns";
+
+SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
+ QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
+ Ui::SettingsDialogBase(),
+ m_appConfig(config)
+{
+ setupUi(this);
+
+ m_Locale.fillLanguageComboBox(m_pComboLanguage);
+
+ m_pLineEditScreenName->setText(appConfig().screenName());
+ m_pSpinBoxPort->setValue(appConfig().port());
+ m_pLineEditInterface->setText(appConfig().networkInterface());
+ m_pComboLogLevel->setCurrentIndex(appConfig().logLevel());
+ m_pCheckBoxLogToFile->setChecked(appConfig().logToFile());
+ m_pLineEditLogFilename->setText(appConfig().logFilename());
+ setIndexFromItemData(m_pComboLanguage, appConfig().language());
+ m_pCheckBoxAutoHide->setChecked(appConfig().getAutoHide());
+ m_pCheckBoxMinimizeToTray->setChecked(appConfig().getMinimizeToTray());
+ m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled());
+
+#if defined(Q_OS_WIN)
+ m_pComboElevate->setCurrentIndex(static_cast<int>(appConfig().elevateMode()));
+#else
+ // elevate checkbox is only useful on ms windows.
+ m_pLabelElevate->hide();
+ m_pComboElevate->hide();
+#endif
+}
+
+void SettingsDialog::accept()
+{
+ m_appConfig.setScreenName(m_pLineEditScreenName->text());
+ m_appConfig.setPort(m_pSpinBoxPort->value());
+ m_appConfig.setNetworkInterface(m_pLineEditInterface->text());
+ m_appConfig.setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked());
+ m_appConfig.setLogLevel(m_pComboLogLevel->currentIndex());
+ m_appConfig.setLogToFile(m_pCheckBoxLogToFile->isChecked());
+ m_appConfig.setLogFilename(m_pLineEditLogFilename->text());
+ m_appConfig.setLanguage(m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString());
+ m_appConfig.setElevateMode(static_cast<ElevateMode>(m_pComboElevate->currentIndex()));
+ m_appConfig.setAutoHide(m_pCheckBoxAutoHide->isChecked());
+ m_appConfig.setMinimizeToTray(m_pCheckBoxMinimizeToTray->isChecked());
+ m_appConfig.saveSettings();
+ QDialog::accept();
+}
+
+void SettingsDialog::reject()
+{
+ if (m_appConfig.language() != m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString()) {
+ QBarrierApplication::getInstance()->switchTranslator(m_appConfig.language());
+ }
+ QDialog::reject();
+}
+
+void SettingsDialog::changeEvent(QEvent* event)
+{
+ if (event != 0)
+ {
+ switch (event->type())
+ {
+ case QEvent::LanguageChange:
+ {
+ int logLevelIndex = m_pComboLogLevel->currentIndex();
+
+ m_pComboLanguage->blockSignals(true);
+ retranslateUi(this);
+ m_pComboLanguage->blockSignals(false);
+
+ m_pComboLogLevel->setCurrentIndex(logLevelIndex);
+ break;
+ }
+
+ default:
+ QDialog::changeEvent(event);
+ }
+ }
+}
+
+void SettingsDialog::on_m_pCheckBoxLogToFile_stateChanged(int i)
+{
+ bool checked = i == 2;
+
+ m_pLineEditLogFilename->setEnabled(checked);
+ m_pButtonBrowseLog->setEnabled(checked);
+}
+
+void SettingsDialog::on_m_pButtonBrowseLog_clicked()
+{
+ QString fileName = QFileDialog::getSaveFileName(
+ this, tr("Save log file to..."),
+ m_pLineEditLogFilename->text(),
+ "Logs (*.log *.txt)");
+
+ if (!fileName.isEmpty())
+ {
+ m_pLineEditLogFilename->setText(fileName);
+ }
+}
+
+void SettingsDialog::on_m_pComboLanguage_currentIndexChanged(int index)
+{
+ QString ietfCode = m_pComboLanguage->itemData(index).toString();
+ QBarrierApplication::getInstance()->switchTranslator(ietfCode);
+} \ No newline at end of file
diff --git a/src/gui/src/SettingsDialog.h b/src/gui/src/SettingsDialog.h
new file mode 100644
index 0000000..c16b821
--- /dev/null
+++ b/src/gui/src/SettingsDialog.h
@@ -0,0 +1,54 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(SETTINGSDIALOG_H)
+
+#define SETTINGSDIALOG_H
+
+#include <QDialog>
+#include "ui_SettingsDialogBase.h"
+#include "BarrierLocale.h"
+#include "CoreInterface.h"
+
+class AppConfig;
+
+class SettingsDialog : public QDialog, public Ui::SettingsDialogBase
+{
+ Q_OBJECT
+
+ public:
+ SettingsDialog(QWidget* parent, AppConfig& config);
+
+ protected:
+ void accept();
+ void reject();
+ void changeEvent(QEvent* event);
+ AppConfig& appConfig() { return m_appConfig; }
+
+ private:
+ AppConfig& m_appConfig;
+ BarrierLocale m_Locale;
+ CoreInterface m_CoreInterface;
+
+ private slots:
+ void on_m_pComboLanguage_currentIndexChanged(int index);
+ void on_m_pCheckBoxLogToFile_stateChanged(int );
+ void on_m_pButtonBrowseLog_clicked();
+};
+
+#endif
diff --git a/src/gui/src/SettingsDialogBase.ui b/src/gui/src/SettingsDialogBase.ui
new file mode 100644
index 0000000..432ec0d
--- /dev/null
+++ b/src/gui/src/SettingsDialogBase.ui
@@ -0,0 +1,368 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SettingsDialogBase</class>
+ <widget class="QDialog" name="SettingsDialogBase">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>368</width>
+ <height>380</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Settings</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="m_pGroupGeneral">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>General</string>
+ </property>
+ <layout class="QFormLayout" name="formLayout">
+ <property name="fieldGrowthPolicy">
+ <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+ </property>
+ <item row="0" column="0">
+ <widget class="QLabel" name="m_pLabel_27">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>75</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>&amp;Language:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pComboLanguage</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="m_pComboLanguage"/>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="m_pLabel_19">
+ <property name="minimumSize">
+ <size>
+ <width>75</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Sc&amp;reen name:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pLineEditScreenName</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="m_pLineEditScreenName">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="m_pLabelElevate">
+ <property name="text">
+ <string>Elevate</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QComboBox" name="m_pComboElevate">
+ <property name="toolTip">
+ <string>Specify when the Barrier service should run at an elevated privilege level</string>
+ </property>
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <item>
+ <property name="text">
+ <string>As Needed</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Always</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Never</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxMinimizeToTray">
+ <property name="text">
+ <string>Minimize to System &amp;Tray</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxAutoHide">
+ <property name="text">
+ <string>&amp;Hide on startup</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="m_pGroupNetworking">
+ <property name="title">
+ <string>Networking</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QLabel" name="m_pLabel_20">
+ <property name="text">
+ <string>P&amp;ort:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pSpinBoxPort</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QSpinBox" name="m_pSpinBoxPort">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximum">
+ <number>65535</number>
+ </property>
+ <property name="value">
+ <number>24800</number>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="m_pLabel_21">
+ <property name="text">
+ <string>&amp;Interface:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pLineEditInterface</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="m_pLineEditInterface">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxEnableCrypto">
+ <property name="text">
+ <string>Enable &amp;SSL</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="m_pGroupLog">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Logging</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QLabel" name="m_pLabel_3">
+ <property name="minimumSize">
+ <size>
+ <width>75</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>&amp;Logging level:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pComboLogLevel</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2">
+ <widget class="QComboBox" name="m_pComboLogLevel">
+ <item>
+ <property name="text">
+ <string>Error</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Warning</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Note</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Info</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Debug2</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="m_pCheckBoxLogToFile">
+ <property name="text">
+ <string>Log to file:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="m_pLineEditLogFilename">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="m_pButtonBrowseLog">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Browse...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>m_pComboLanguage</tabstop>
+ <tabstop>m_pLineEditScreenName</tabstop>
+ <tabstop>m_pComboElevate</tabstop>
+ <tabstop>m_pCheckBoxMinimizeToTray</tabstop>
+ <tabstop>m_pCheckBoxAutoHide</tabstop>
+ <tabstop>m_pSpinBoxPort</tabstop>
+ <tabstop>m_pLineEditInterface</tabstop>
+ <tabstop>m_pCheckBoxEnableCrypto</tabstop>
+ <tabstop>m_pComboLogLevel</tabstop>
+ <tabstop>m_pCheckBoxLogToFile</tabstop>
+ <tabstop>m_pLineEditLogFilename</tabstop>
+ <tabstop>m_pButtonBrowseLog</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>SettingsDialogBase</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>266</x>
+ <y>340</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>SettingsDialogBase</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>334</x>
+ <y>340</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/gui/src/SetupWizard.cpp b/src/gui/src/SetupWizard.cpp
new file mode 100644
index 0000000..313e48b
--- /dev/null
+++ b/src/gui/src/SetupWizard.cpp
@@ -0,0 +1,149 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-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 "SetupWizard.h"
+#include "MainWindow.h"
+#include "WebClient.h"
+#include "QBarrierApplication.h"
+#include "QUtility.h"
+
+#include <QMessageBox>
+
+SetupWizard::SetupWizard(MainWindow& mainWindow, bool startMain) :
+ m_MainWindow(mainWindow),
+ m_StartMain(startMain)
+{
+ setupUi(this);
+
+#if defined(Q_OS_MAC)
+
+ // the mac style needs a little more room because of the
+ // graphic on the left.
+ resize(600, 500);
+ setMinimumSize(size());
+
+#elif defined(Q_OS_WIN)
+
+ // when areo is disabled on windows, the next/back buttons
+ // are hidden (must be a qt bug) -- resizing the window
+ // to +1 of the original height seems to fix this.
+ // NOTE: calling setMinimumSize after this will break
+ // it again, so don't do that.
+ resize(size().width(), size().height() + 1);
+
+#endif
+
+ connect(m_pServerRadioButton, SIGNAL(toggled(bool)), m_MainWindow.m_pGroupServer, SLOT(setChecked(bool)));
+ connect(m_pClientRadioButton, SIGNAL(toggled(bool)), m_MainWindow.m_pGroupClient, SLOT(setChecked(bool)));
+
+ m_Locale.fillLanguageComboBox(m_pComboLanguage);
+ setIndexFromItemData(m_pComboLanguage, m_MainWindow.appConfig().language());
+}
+
+SetupWizard::~SetupWizard()
+{
+}
+
+bool SetupWizard::validateCurrentPage()
+{
+ QMessageBox message;
+ message.setWindowTitle(tr("Setup Barrier"));
+ message.setIcon(QMessageBox::Information);
+
+ if (currentPage() == m_pNodePage)
+ {
+ bool result = m_pClientRadioButton->isChecked() ||
+ m_pServerRadioButton->isChecked();
+
+ if (!result)
+ {
+ message.setText(tr("Please select an option."));
+ message.exec();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void SetupWizard::changeEvent(QEvent* event)
+{
+ if (event != 0)
+ {
+ switch (event->type())
+ {
+ case QEvent::LanguageChange:
+ {
+ m_pComboLanguage->blockSignals(true);
+ retranslateUi(this);
+ m_pComboLanguage->blockSignals(false);
+ break;
+ }
+
+ default:
+ QWizard::changeEvent(event);
+ }
+ }
+}
+
+void SetupWizard::accept()
+{
+ AppConfig& appConfig = m_MainWindow.appConfig();
+
+ appConfig.setLanguage(m_pComboLanguage->itemData(m_pComboLanguage->currentIndex()).toString());
+
+ appConfig.setWizardHasRun();
+ appConfig.saveSettings();
+
+ QSettings& settings = m_MainWindow.settings();
+ if (m_pServerRadioButton->isChecked())
+ {
+ settings.setValue("groupServerChecked", true);
+ settings.setValue("groupClientChecked", false);
+ }
+ if (m_pClientRadioButton->isChecked())
+ {
+ settings.setValue("groupClientChecked", true);
+ settings.setValue("groupServerChecked", false);
+ }
+
+ QWizard::accept();
+
+ if (m_StartMain)
+ {
+ m_MainWindow.updateZeroconfService();
+ m_MainWindow.open();
+ }
+}
+
+void SetupWizard::reject()
+{
+ QBarrierApplication::getInstance()->switchTranslator(m_MainWindow.appConfig().language());
+
+ if (m_StartMain)
+ {
+ m_MainWindow.open();
+ }
+
+ QWizard::reject();
+}
+
+void SetupWizard::on_m_pComboLanguage_currentIndexChanged(int index)
+{
+ QString ietfCode = m_pComboLanguage->itemData(index).toString();
+ QBarrierApplication::getInstance()->switchTranslator(ietfCode);
+}
diff --git a/src/gui/src/SetupWizard.h b/src/gui/src/SetupWizard.h
new file mode 100644
index 0000000..80e19e9
--- /dev/null
+++ b/src/gui/src/SetupWizard.h
@@ -0,0 +1,53 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-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/>.
+ */
+
+#pragma once
+
+#include "ui_SetupWizardBase.h"
+#include "BarrierLocale.h"
+
+#include <QWizard>
+#include <QNetworkAccessManager>
+
+class MainWindow;
+
+class SetupWizard : public QWizard, public Ui::SetupWizardBase
+{
+ Q_OBJECT
+public:
+ enum {
+ kMaximiumLoginAttemps = 3
+ };
+
+public:
+ SetupWizard(MainWindow& mainWindow, bool startMain);
+ virtual ~SetupWizard();
+ bool validateCurrentPage();
+
+protected:
+ void changeEvent(QEvent* event);
+ void accept();
+ void reject();
+
+private:
+ MainWindow& m_MainWindow;
+ bool m_StartMain;
+ BarrierLocale m_Locale;
+
+private slots:
+ void on_m_pComboLanguage_currentIndexChanged(int index);
+};
diff --git a/src/gui/src/SetupWizardBase.ui b/src/gui/src/SetupWizardBase.ui
new file mode 100644
index 0000000..47de563
--- /dev/null
+++ b/src/gui/src/SetupWizardBase.ui
@@ -0,0 +1,245 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SetupWizardBase</class>
+ <widget class="QWizard" name="SetupWizardBase">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>556</width>
+ <height>464</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>500</width>
+ <height>390</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Setup Barrier</string>
+ </property>
+ <widget class="QWizardPage" name="m_pWelcomePage">
+ <property name="title">
+ <string>Welcome</string>
+ </property>
+ <property name="subTitle">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Thanks for installing Barrier!</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QFormLayout" name="formLayout_3">
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_4">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string notr="true">&amp;Language:</string>
+ </property>
+ <property name="buddy">
+ <cstring>m_pComboLanguage</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="m_pComboLanguage">
+ <property name="maximumSize">
+ <size>
+ <width>200</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_7">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="label_5">
+ <property name="locale">
+ <locale language="English" country="UnitedKingdom"/>
+ </property>
+ <property name="text">
+ <string>Barrier lets you easily share your mouse and keyboard between multiple computers on your desk, and it's Free and Open Source. Just move your mouse off the edge of one computer's screen on to another. You can even share all of your clipboards. All you need is a network connection. Barrier is cross-platform (works on Windows, Mac OS X and Linux).</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWizardPage" name="m_pNodePage">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Server or Client?</string>
+ </property>
+ <property name="subTitle">
+ <string/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QRadioButton" name="m_pServerRadioButton">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>&amp;Server (share this computer's mouse and keyboard)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_pClientLabel">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;My main mouse and keyboard are connected to this computer. This will allow you to move your mouse over to another computer's screen. There can only be one server in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="m_pClientRadioButton">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>&amp;Client (use another computer's mouse and keyboard)</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="m_pServerLabel">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:8pt;&quot;&gt;You have already set up a server. This computer will be controlled using the server's mouse and keyboard. There can be many clients in your setup.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::MinimumExpanding</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ <tabstops>
+ <tabstop>m_pComboLanguage</tabstop>
+ <tabstop>m_pServerRadioButton</tabstop>
+ <tabstop>m_pClientRadioButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/gui/src/ShutdownCh.h b/src/gui/src/ShutdownCh.h
new file mode 100644
index 0000000..2462cae
--- /dev/null
+++ b/src/gui/src/ShutdownCh.h
@@ -0,0 +1,22 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2018 Debauchee Open Source Group
+ *
+ * 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
+
+// included in both the GUI and the child apps (server & client)
+const char ShutdownCh = 'S';
+
diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp
new file mode 100644
index 0000000..7de7eaa
--- /dev/null
+++ b/src/gui/src/SslCertificate.cpp
@@ -0,0 +1,178 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "SslCertificate.h"
+
+#include "Fingerprint.h"
+
+#include <QProcess>
+#include <QDir>
+#include <QCoreApplication>
+
+static const char kCertificateLifetime[] = "365";
+static const char kCertificateSubjectInfo[] = "/CN=Barrier";
+static const char kCertificateFilename[] = "Barrier.pem";
+static const char kSslDir[] = "SSL";
+static const char kUnixOpenSslCommand[] = "openssl";
+
+#if defined(Q_OS_WIN)
+static const char kWinOpenSslBinary[] = "openssl.exe";
+static const char kConfigFile[] = "barrier.conf";
+#endif
+
+SslCertificate::SslCertificate(QObject *parent) :
+ QObject(parent)
+{
+ m_ProfileDir = m_CoreInterface.getProfileDir();
+ if (m_ProfileDir.isEmpty()) {
+ emit error(tr("Failed to get profile directory."));
+ }
+}
+
+bool SslCertificate::runTool(const QStringList& args)
+{
+ QString program;
+#if defined(Q_OS_WIN)
+ program = QCoreApplication::applicationDirPath();
+ program.append("\\").append(kWinOpenSslBinary);
+#else
+ program = kUnixOpenSslCommand;
+#endif
+
+
+ QStringList environment;
+#if defined(Q_OS_WIN)
+ environment << QString("OPENSSL_CONF=%1\\%2")
+ .arg(QCoreApplication::applicationDirPath())
+ .arg(kConfigFile);
+#endif
+
+ QProcess process;
+ process.setEnvironment(environment);
+ process.start(program, args);
+
+ bool success = process.waitForStarted();
+
+ QString standardError;
+ if (success && process.waitForFinished())
+ {
+ m_ToolOutput = process.readAllStandardOutput().trimmed();
+ standardError = process.readAllStandardError().trimmed();
+ }
+
+ int code = process.exitCode();
+ if (!success || code != 0)
+ {
+ emit error(
+ QString("SSL tool failed: %1\n\nCode: %2\nError: %3")
+ .arg(program)
+ .arg(process.exitCode())
+ .arg(standardError.isEmpty() ? "Unknown" : standardError));
+ return false;
+ }
+
+ return true;
+}
+
+void SslCertificate::generateCertificate()
+{
+ QString sslDirPath = QString("%1%2%3")
+ .arg(m_ProfileDir)
+ .arg(QDir::separator())
+ .arg(kSslDir);
+
+ QString filename = QString("%1%2%3")
+ .arg(sslDirPath)
+ .arg(QDir::separator())
+ .arg(kCertificateFilename);
+
+ QFile file(filename);
+ if (!file.exists()) {
+ QStringList arguments;
+
+ // self signed certificate
+ arguments.append("req");
+ arguments.append("-x509");
+ arguments.append("-nodes");
+
+ // valide duration
+ arguments.append("-days");
+ arguments.append(kCertificateLifetime);
+
+ // subject information
+ arguments.append("-subj");
+
+ QString subInfo(kCertificateSubjectInfo);
+ arguments.append(subInfo);
+
+ // private key
+ arguments.append("-newkey");
+ arguments.append("rsa:1024");
+
+ QDir sslDir(sslDirPath);
+ if (!sslDir.exists()) {
+ sslDir.mkpath(".");
+ }
+
+ // key output filename
+ arguments.append("-keyout");
+ arguments.append(filename);
+
+ // certificate output filename
+ arguments.append("-out");
+ arguments.append(filename);
+
+ if (!runTool(arguments)) {
+ return;
+ }
+
+ emit info(tr("SSL certificate generated."));
+ }
+
+ generateFingerprint(filename);
+
+ emit generateFinished();
+}
+
+void SslCertificate::generateFingerprint(const QString& certificateFilename)
+{
+ QStringList arguments;
+ arguments.append("x509");
+ arguments.append("-fingerprint");
+ arguments.append("-sha1");
+ arguments.append("-noout");
+ arguments.append("-in");
+ arguments.append(certificateFilename);
+
+ if (!runTool(arguments)) {
+ return;
+ }
+
+ // find the fingerprint from the tool output
+ int i = m_ToolOutput.indexOf("=");
+ if (i != -1) {
+ i++;
+ QString fingerprint = m_ToolOutput.mid(
+ i, m_ToolOutput.size() - i);
+
+ Fingerprint::local().trust(fingerprint, false);
+ emit info(tr("SSL fingerprint generated."));
+ }
+ else {
+ emit error(tr("Failed to find SSL fingerprint."));
+ }
+}
diff --git a/src/gui/src/SslCertificate.h b/src/gui/src/SslCertificate.h
new file mode 100644
index 0000000..8acda4b
--- /dev/null
+++ b/src/gui/src/SslCertificate.h
@@ -0,0 +1,47 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+#include "CoreInterface.h"
+
+#include <QObject>
+
+class SslCertificate : public QObject
+{
+Q_OBJECT
+
+public:
+ explicit SslCertificate(QObject *parent = 0);
+
+public slots:
+ void generateCertificate();
+
+signals:
+ void error(QString e);
+ void info(QString i);
+ void generateFinished();
+
+private:
+ bool runTool(const QStringList& args);
+ void generateFingerprint(const QString& certificateFilename);
+
+private:
+ QString m_ProfileDir;
+ QString m_ToolOutput;
+ CoreInterface m_CoreInterface;
+};
diff --git a/src/gui/src/TrashScreenWidget.cpp b/src/gui/src/TrashScreenWidget.cpp
new file mode 100644
index 0000000..42a9d56
--- /dev/null
+++ b/src/gui/src/TrashScreenWidget.cpp
@@ -0,0 +1,43 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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 "TrashScreenWidget.h"
+#include "ScreenSetupModel.h"
+
+#include <QtCore>
+#include <QtGui>
+
+void TrashScreenWidget::dragEnterEvent(QDragEnterEvent* event)
+{
+ if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType()))
+ {
+ event->setDropAction(Qt::MoveAction);
+ event->accept();
+ }
+ else
+ event->ignore();
+}
+
+void TrashScreenWidget::dropEvent(QDropEvent* event)
+{
+ if (event->mimeData()->hasFormat(ScreenSetupModel::mimeType()))
+ event->acceptProposedAction();
+ else
+ event->ignore();
+}
+
diff --git a/src/gui/src/TrashScreenWidget.h b/src/gui/src/TrashScreenWidget.h
new file mode 100644
index 0000000..7ab887e
--- /dev/null
+++ b/src/gui/src/TrashScreenWidget.h
@@ -0,0 +1,42 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#if !defined(TRASHSCREENWIDGET__H)
+
+#define TRASHSCREENWIDGET__H
+
+#include <QLabel>
+
+class QWidget;
+class QDragEnterEvent;
+class QDropEvent;
+
+class TrashScreenWidget : public QLabel
+{
+ Q_OBJECT
+
+ public:
+ TrashScreenWidget(QWidget* parent) : QLabel(parent) {}
+
+ public:
+ void dragEnterEvent(QDragEnterEvent* event);
+ void dropEvent(QDropEvent* event);
+};
+
+#endif
+
diff --git a/src/gui/src/VersionChecker.cpp b/src/gui/src/VersionChecker.cpp
new file mode 100644
index 0000000..b680309
--- /dev/null
+++ b/src/gui/src/VersionChecker.cpp
@@ -0,0 +1,111 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "VersionChecker.h"
+
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <QProcess>
+#include <QLocale>
+
+#define VERSION_REGEX "(\\d+\\.\\d+\\.\\d+)"
+//#define VERSION_URL "http://www.TODO.com/"
+
+VersionChecker::VersionChecker(QObject* parent)
+ : QObject(parent)
+{
+}
+
+void VersionChecker::checkLatest()
+{
+ // calling m_manager->get(..) is causing an access violation on app close
+ // atm there is nothing to check the version against, so removing until we need a version checker again
+
+ //m_manager = new QNetworkAccessManager(this);
+
+ //connect(m_manager, SIGNAL(finished(QNetworkReply*)),
+ // this, SLOT(replyFinished(QNetworkReply*)));
+
+ //m_manager->get(QNetworkRequest(QUrl(VERSION_URL)));
+}
+
+void VersionChecker::replyFinished(QNetworkReply* reply)
+{
+ if (reply->error()) {
+ // TODO: handle me
+ } else {
+ QString newestVersion = QString(reply->readAll());
+ if (!newestVersion.isEmpty()) {
+ QString currentVersion = getVersion();
+ if (currentVersion != "Unknown") {
+ if (compareVersions(currentVersion, newestVersion) > 0)
+ emit updateFound(newestVersion);
+ }
+ }
+ }
+ reply->deleteLater();
+}
+
+int VersionChecker::compareVersions(const QString& left, const QString& right)
+{
+ if (left.compare(right) == 0)
+ return 0; // versions are same.
+
+ QStringList leftSplit = left.split(QRegExp("\\."));
+ if (leftSplit.size() != 3)
+ return 1; // assume right wins.
+
+ QStringList rightSplit = right.split(QRegExp("\\."));
+ if (rightSplit.size() != 3)
+ return -1; // assume left wins.
+
+ int leftMajor = leftSplit.at(0).toInt();
+ int leftMinor = leftSplit.at(1).toInt();
+ int leftRev = leftSplit.at(2).toInt();
+
+ int rightMajor = rightSplit.at(0).toInt();
+ int rightMinor = rightSplit.at(1).toInt();
+ int rightRev = rightSplit.at(2).toInt();
+
+ bool rightWins =
+ (rightMajor > leftMajor) ||
+ ((rightMajor >= leftMajor) && (rightMinor > leftMinor)) ||
+ ((rightMajor >= leftMajor) && (rightMinor >= leftMinor) && (rightRev > leftRev));
+
+ return rightWins ? 1 : -1;
+}
+
+QString VersionChecker::getVersion()
+{
+ QProcess process;
+ process.start(m_app, QStringList() << "--version");
+
+ process.setReadChannel(QProcess::StandardOutput);
+ if (process.waitForStarted() && process.waitForFinished())
+ {
+ QRegExp rx(VERSION_REGEX);
+ QString text = process.readLine();
+ if (rx.indexIn(text) != -1)
+ {
+ return rx.cap(1);
+ }
+ }
+
+ return tr("Unknown");
+}
diff --git a/src/gui/src/VersionChecker.h b/src/gui/src/VersionChecker.h
new file mode 100644
index 0000000..e4df2c6
--- /dev/null
+++ b/src/gui/src/VersionChecker.h
@@ -0,0 +1,43 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 <QObject>
+#include <QString>
+
+class QNetworkAccessManager;
+class QNetworkReply;
+
+class VersionChecker : public QObject
+{
+ Q_OBJECT
+public:
+ explicit VersionChecker(QObject* parent = 0);
+ void checkLatest();
+ QString getVersion();
+ void setApp(const QString& app) { m_app = app; }
+ int compareVersions(const QString& left, const QString& right);
+public slots:
+ void replyFinished(QNetworkReply* reply);
+signals:
+ void updateFound(const QString& version);
+private:
+ QNetworkAccessManager* m_manager;
+ QString m_app;
+};
diff --git a/src/gui/src/WebClient.cpp b/src/gui/src/WebClient.cpp
new file mode 100644
index 0000000..8cded2c
--- /dev/null
+++ b/src/gui/src/WebClient.cpp
@@ -0,0 +1,83 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "WebClient.h"
+
+#include "QUtility.h"
+
+#include <QProcess>
+#include <QMessageBox>
+#include <QCoreApplication>
+#include <stdexcept>
+
+bool
+WebClient::getEdition (int& edition, QString& errorOut) {
+ QString responseJson = request();
+
+ /* TODO: This is horrible and should be ripped out as soon as we move
+ * to Qt 5. See issue #5630
+ */
+
+ QRegExp resultRegex(".*\"result\".*:.*(true|false).*");
+ if (resultRegex.exactMatch (responseJson)) {
+ QString boolString = resultRegex.cap(1);
+ if (boolString == "true") {
+ QRegExp editionRegex(".*\"edition\".*:.*\"([^\"]+)\".*");
+ if (editionRegex.exactMatch(responseJson)) {
+ QString e = editionRegex.cap(1);
+ edition = e.toInt();
+ return true;
+ } else {
+ throw std::runtime_error ("Unrecognised server response.");
+ }
+ } else {
+ errorOut = tr("Login failed. Invalid email address or password.");
+ return false;
+ }
+ } else {
+ QRegExp errorRegex(".*\"error\".*:.*\"([^\"]+)\".*");
+ if (errorRegex.exactMatch (responseJson)) {
+ errorOut = errorRegex.cap(1).replace("\\n", "\n");
+ return false;
+ } else {
+ throw std::runtime_error ("Unrecognised server response.");
+ }
+ }
+}
+
+bool
+WebClient::setEmail (QString email, QString& errorOut) {
+ if (email.isEmpty()) {
+ errorOut = tr("Your email address cannot be left blank.");
+ return false;
+ }
+ m_Email = email;
+ return true;
+}
+
+bool
+WebClient::setPassword (QString password, QString&) {
+ m_Password = password;
+ return true;
+}
+
+QString
+WebClient::request() {
+ QStringList args("--login-auth");
+ QString credentials (m_Email + ":" + hash(m_Password) + "\n");
+ return m_CoreInterface.run (args, credentials);
+}
diff --git a/src/gui/src/WebClient.h b/src/gui/src/WebClient.h
new file mode 100644
index 0000000..9874bd5
--- /dev/null
+++ b/src/gui/src/WebClient.h
@@ -0,0 +1,49 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#ifndef WEBCLIENT_H
+#define WEBCLIENT_H
+
+#include <QString>
+#include <QObject>
+
+#include "CoreInterface.h"
+
+class QMessageBox;
+class QWidget;
+class QStringList;
+
+class WebClient : public QObject
+{
+ Q_OBJECT
+
+public:
+ bool getEdition (int& edition, QString& errorOut);
+ bool setEmail (QString email, QString& errorOut);
+ bool setPassword (QString password, QString& errorOut);
+signals:
+ void error(QString e);
+
+private:
+ QString request();
+
+ QString m_Email;
+ QString m_Password;
+ CoreInterface m_CoreInterface;
+};
+
+#endif // WEBCLIENT_H
diff --git a/src/gui/src/ZeroconfBrowser.cpp b/src/gui/src/ZeroconfBrowser.cpp
new file mode 100644
index 0000000..35b44c1
--- /dev/null
+++ b/src/gui/src/ZeroconfBrowser.cpp
@@ -0,0 +1,92 @@
+/*
+ * barrier -- 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 "ZeroconfBrowser.h"
+
+#include <QtCore/QSocketNotifier>
+
+ZeroconfBrowser::ZeroconfBrowser(QObject* parent) :
+ QObject(parent),
+ m_DnsServiceRef(0),
+ m_pSocket(0)
+{
+}
+
+ZeroconfBrowser::~ZeroconfBrowser()
+{
+ if (m_pSocket) {
+ delete m_pSocket;
+ }
+
+ if (m_DnsServiceRef) {
+ DNSServiceRefDeallocate(m_DnsServiceRef);
+ m_DnsServiceRef = 0;
+ }
+}
+
+void ZeroconfBrowser::browseForType(const QString& type)
+{
+ DNSServiceErrorType err = DNSServiceBrowse(&m_DnsServiceRef, 0, 0,
+ type.toUtf8().constData(), 0, browseReply, this);
+
+ if (err != kDNSServiceErr_NoError) {
+ emit error(err);
+ }
+ else {
+ int sockFD = DNSServiceRefSockFD(m_DnsServiceRef);
+ if (sockFD == -1) {
+ emit error(kDNSServiceErr_Invalid);
+ }
+ else {
+ m_pSocket = new QSocketNotifier(sockFD, QSocketNotifier::Read, this);
+ connect(m_pSocket, SIGNAL(activated(int)), this,
+ SLOT(socketReadyRead()));
+ }
+ }
+}
+
+void ZeroconfBrowser::socketReadyRead()
+{
+ DNSServiceErrorType err = DNSServiceProcessResult(m_DnsServiceRef);
+ if (err != kDNSServiceErr_NoError) {
+ emit error(err);
+ }
+}
+
+void ZeroconfBrowser::browseReply(DNSServiceRef, DNSServiceFlags flags,
+ quint32, DNSServiceErrorType errorCode, const char* serviceName,
+ const char* regType, const char* replyDomain, void* context)
+{
+ ZeroconfBrowser* browser = static_cast<ZeroconfBrowser*>(context);
+ if (errorCode != kDNSServiceErr_NoError) {
+ emit browser->error(errorCode);
+ }
+ else {
+ ZeroconfRecord record(serviceName, regType, replyDomain);
+ if (flags & kDNSServiceFlagsAdd) {
+ if (!browser->m_Records.contains(record)) {
+ browser->m_Records.append(record);
+ }
+ }
+ else {
+ browser->m_Records.removeAll(record);
+ }
+ if (!(flags & kDNSServiceFlagsMoreComing)) {
+ emit browser->currentRecordsChanged(browser->m_Records);
+ }
+ }
+}
diff --git a/src/gui/src/ZeroconfBrowser.h b/src/gui/src/ZeroconfBrowser.h
new file mode 100644
index 0000000..755907e
--- /dev/null
+++ b/src/gui/src/ZeroconfBrowser.h
@@ -0,0 +1,57 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include "ZeroconfRecord.h"
+
+#include <QtCore/QObject>
+#define _MSL_STDINT_H
+#include <stdint.h>
+#include <dns_sd.h>
+
+class QSocketNotifier;
+
+class ZeroconfBrowser : public QObject
+{
+ Q_OBJECT
+
+public:
+ ZeroconfBrowser(QObject* parent = 0);
+ ~ZeroconfBrowser();
+ void browseForType(const QString& type);
+ inline QList<ZeroconfRecord> currentRecords() const { return m_Records; }
+ inline QString serviceType() const { return m_BrowsingType; }
+
+signals:
+ void currentRecordsChanged(const QList<ZeroconfRecord>& list);
+ void error(DNSServiceErrorType err);
+
+private slots:
+ void socketReadyRead();
+
+private:
+ static void DNSSD_API browseReply(DNSServiceRef, DNSServiceFlags flags,
+ quint32, DNSServiceErrorType errorCode, const char* serviceName,
+ const char* regType, const char* replyDomain, void* context);
+
+private:
+ DNSServiceRef m_DnsServiceRef;
+ QSocketNotifier* m_pSocket;
+ QList<ZeroconfRecord> m_Records;
+ QString m_BrowsingType;
+};
diff --git a/src/gui/src/ZeroconfRecord.h b/src/gui/src/ZeroconfRecord.h
new file mode 100644
index 0000000..eba7785
--- /dev/null
+++ b/src/gui/src/ZeroconfRecord.h
@@ -0,0 +1,50 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include <QtCore/QMetaType>
+#include <QtCore/QString>
+
+class ZeroconfRecord
+{
+public:
+ ZeroconfRecord() {}
+ ZeroconfRecord(const QString& name, const QString& regType,
+ const QString& domain)
+ : serviceName(name), registeredType(regType), replyDomain(domain)
+ {}
+ ZeroconfRecord(const char* name, const char* regType, const char* domain)
+ {
+ serviceName = QString::fromUtf8(name);
+ registeredType = QString::fromUtf8(regType);
+ replyDomain = QString::fromUtf8(domain);
+ }
+
+ bool operator==(const ZeroconfRecord& other) const {
+ return serviceName == other.serviceName
+ && registeredType == other.registeredType
+ && replyDomain == other.replyDomain;
+ }
+
+public:
+ QString serviceName;
+ QString registeredType;
+ QString replyDomain;
+};
+
+Q_DECLARE_METATYPE(ZeroconfRecord)
diff --git a/src/gui/src/ZeroconfRegister.cpp b/src/gui/src/ZeroconfRegister.cpp
new file mode 100644
index 0000000..6d91a48
--- /dev/null
+++ b/src/gui/src/ZeroconfRegister.cpp
@@ -0,0 +1,94 @@
+/*
+ * barrier -- 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 "ZeroconfRegister.h"
+
+#include <QtCore/QSocketNotifier>
+
+ZeroconfRegister::ZeroconfRegister(QObject* parent) :
+ QObject(parent),
+ m_DnsServiceRef(0),
+ m_pSocket(0)
+{
+}
+
+ZeroconfRegister::~ZeroconfRegister()
+{
+ if (m_pSocket) {
+ delete m_pSocket;
+ }
+
+ if (m_DnsServiceRef) {
+ DNSServiceRefDeallocate(m_DnsServiceRef);
+ m_DnsServiceRef = 0;
+ }
+}
+
+void ZeroconfRegister::registerService(const ZeroconfRecord& record,
+ quint16 servicePort)
+{
+ if (m_DnsServiceRef) {
+ qWarning("Warning: Already registered a service for this object");
+ return;
+ }
+
+ quint16 bigEndianPort = servicePort;
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ {
+ bigEndianPort = 0 | ((servicePort & 0x00ff) << 8) | ((servicePort & 0xff00) >> 8);
+ }
+#endif
+
+ DNSServiceErrorType err = DNSServiceRegister(&m_DnsServiceRef,
+ kDNSServiceFlagsNoAutoRename, 0,
+ record.serviceName.toUtf8().constData(),
+ record.registeredType.toUtf8().constData(),
+ record.replyDomain.isEmpty() ? 0 : record.replyDomain.toUtf8().constData(),
+ 0, bigEndianPort, 0, 0, registerService, this);
+
+ if (err != kDNSServiceErr_NoError) {
+ emit error(err);
+ }
+ else {
+ int sockfd = DNSServiceRefSockFD(m_DnsServiceRef);
+ if (sockfd == -1) {
+ emit error(kDNSServiceErr_Invalid);
+ }
+ else {
+ m_pSocket = new QSocketNotifier(sockfd, QSocketNotifier::Read, this);
+ connect(m_pSocket, SIGNAL(activated(int)), this, SLOT(socketReadyRead()));
+ }
+ }
+}
+
+void ZeroconfRegister::socketReadyRead()
+{
+ DNSServiceErrorType err = DNSServiceProcessResult(m_DnsServiceRef);
+ if (err != kDNSServiceErr_NoError) {
+ emit error(err);
+ }
+}
+
+void ZeroconfRegister::registerService(DNSServiceRef, DNSServiceFlags,
+ DNSServiceErrorType errorCode, const char* name, const char* regtype,
+ const char* domain, void* data)
+{
+ ZeroconfRegister* serviceRegister = static_cast<ZeroconfRegister*>(data);
+ if (errorCode != kDNSServiceErr_NoError) {
+ emit serviceRegister->error(errorCode);
+ }
+}
diff --git a/src/gui/src/ZeroconfRegister.h b/src/gui/src/ZeroconfRegister.h
new file mode 100644
index 0000000..e4f3a78
--- /dev/null
+++ b/src/gui/src/ZeroconfRegister.h
@@ -0,0 +1,61 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include <QtCore/QObject>
+
+#include "ZeroconfRecord.h"
+
+class QSocketNotifier;
+
+// Bonjour flags
+#define _MSL_STDINT_H
+#include <stdint.h>
+#if defined(Q_OS_WIN)
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <dns_sd.h>
+
+class ZeroconfRegister : public QObject
+{
+ Q_OBJECT
+
+public:
+ ZeroconfRegister(QObject* parent = 0);
+ ~ZeroconfRegister();
+
+ void registerService(const ZeroconfRecord& record, quint16 servicePort);
+ inline ZeroconfRecord registeredRecord() const { return finalRecord; }
+
+signals:
+ void error(DNSServiceErrorType error);
+ void serviceRegistered(const ZeroconfRecord& record);
+
+private slots:
+ void socketReadyRead();
+
+private:
+ static void DNSSD_API registerService(DNSServiceRef sdRef,
+ DNSServiceFlags, DNSServiceErrorType errorCode, const char* name,
+ const char* regtype, const char* domain, void* context);
+
+private:
+ DNSServiceRef m_DnsServiceRef;
+ QSocketNotifier* m_pSocket;
+ ZeroconfRecord finalRecord;
+};
diff --git a/src/gui/src/ZeroconfServer.cpp b/src/gui/src/ZeroconfServer.cpp
new file mode 100644
index 0000000..40b97a5
--- /dev/null
+++ b/src/gui/src/ZeroconfServer.cpp
@@ -0,0 +1,33 @@
+/*
+ * barrier -- 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 "ZeroconfServer.h"
+#include "ZeroconfThread.h"
+
+#include <stdlib.h>
+
+ZeroconfServer::ZeroconfServer(QObject* parent) :
+ QTcpServer(parent)
+{
+}
+
+void ZeroconfServer::incomingConnection(qintptr socketDescriptor)
+{
+ ZeroconfThread* thread = new ZeroconfThread(socketDescriptor, this);
+ connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+ thread->start();
+}
diff --git a/src/gui/src/ZeroconfServer.h b/src/gui/src/ZeroconfServer.h
new file mode 100644
index 0000000..2e906d9
--- /dev/null
+++ b/src/gui/src/ZeroconfServer.h
@@ -0,0 +1,37 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include <QStringList>
+#include <QTcpServer>
+
+class ZeroconfRegister;
+
+class ZeroconfServer : public QTcpServer
+{
+ Q_OBJECT
+
+public:
+ ZeroconfServer(QObject* parent = 0);
+
+protected:
+ void incomingConnection(qintptr socketDescriptor) override;
+
+private:
+ QStringList fortunes;
+};
diff --git a/src/gui/src/ZeroconfService.cpp b/src/gui/src/ZeroconfService.cpp
new file mode 100644
index 0000000..02902ee
--- /dev/null
+++ b/src/gui/src/ZeroconfService.cpp
@@ -0,0 +1,188 @@
+/*
+ * barrier -- 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 "ZeroconfService.h"
+
+#include "MainWindow.h"
+#include "ZeroconfRegister.h"
+#include "ZeroconfBrowser.h"
+
+#include <QtNetwork>
+#include <QMessageBox>
+#define _MSL_STDINT_H
+#include <stdint.h>
+#include <dns_sd.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#else
+#include <stdlib.h>
+#endif
+
+static const QStringList preferedIPAddress(
+ QStringList() <<
+ "192.168." <<
+ "10." <<
+ "172.");
+
+const char* ZeroconfService:: m_ServerServiceName = "_barrierServerZeroconf._tcp";
+const char* ZeroconfService:: m_ClientServiceName = "_barrierClientZeroconf._tcp";
+
+static void silence_avahi_warning()
+{
+ // the libavahi folks seemingly find Apple's bonjour API distasteful
+ // and are quite liberal in taking it out on users...unless we set
+ // this environmental variable before calling the avahi library.
+ // additionally, Microsoft does not give us a POSIX setenv() so
+ // we use their OS-specific API instead
+ const char *name = "AVAHI_COMPAT_NOWARN";
+ const char *value = "1";
+#ifdef _WIN32
+ SetEnvironmentVariable(name, value);
+#else
+ setenv(name, value, 1);
+#endif
+}
+
+ZeroconfService::ZeroconfService(MainWindow* mainWindow) :
+ m_pMainWindow(mainWindow),
+ m_pZeroconfBrowser(0),
+ m_pZeroconfRegister(0),
+ m_ServiceRegistered(false)
+{
+ silence_avahi_warning();
+ if (m_pMainWindow->barrierType() == MainWindow::barrierServer) {
+ if (registerService(true)) {
+ m_pZeroconfBrowser = new ZeroconfBrowser(this);
+ connect(m_pZeroconfBrowser, SIGNAL(
+ currentRecordsChanged(const QList<ZeroconfRecord>&)),
+ this, SLOT(clientDetected(const QList<ZeroconfRecord>&)));
+ m_pZeroconfBrowser->browseForType(
+ QLatin1String(m_ClientServiceName));
+ }
+ }
+ else {
+ m_pZeroconfBrowser = new ZeroconfBrowser(this);
+ connect(m_pZeroconfBrowser, SIGNAL(
+ currentRecordsChanged(const QList<ZeroconfRecord>&)),
+ this, SLOT(serverDetected(const QList<ZeroconfRecord>&)));
+ m_pZeroconfBrowser->browseForType(
+ QLatin1String(m_ServerServiceName));
+ }
+
+ connect(m_pZeroconfBrowser, SIGNAL(error(DNSServiceErrorType)),
+ this, SLOT(errorHandle(DNSServiceErrorType)));
+}
+
+ZeroconfService::~ZeroconfService()
+{
+ if (m_pZeroconfBrowser) {
+ delete m_pZeroconfBrowser;
+ }
+ if (m_pZeroconfRegister) {
+ delete m_pZeroconfRegister;
+ }
+}
+
+void ZeroconfService::serverDetected(const QList<ZeroconfRecord>& list)
+{
+ foreach (ZeroconfRecord record, list) {
+ registerService(false);
+ m_pMainWindow->appendLogInfo(tr("zeroconf server detected: %1").arg(
+ record.serviceName));
+ m_pMainWindow->serverDetected(record.serviceName);
+ }
+}
+
+void ZeroconfService::clientDetected(const QList<ZeroconfRecord>& list)
+{
+ foreach (ZeroconfRecord record, list) {
+ m_pMainWindow->appendLogInfo(tr("zeroconf client detected: %1").arg(
+ record.serviceName));
+ m_pMainWindow->autoAddScreen(record.serviceName);
+ }
+}
+
+void ZeroconfService::errorHandle(DNSServiceErrorType errorCode)
+{
+ QMessageBox::critical(0, tr("Zero configuration service"),
+ tr("Error code: %1.").arg(errorCode));
+}
+
+QString ZeroconfService::getLocalIPAddresses()
+{
+ QStringList addresses;
+ foreach (const QHostAddress& address, QNetworkInterface::allAddresses()) {
+ if (address.protocol() == QAbstractSocket::IPv4Protocol &&
+ address != QHostAddress(QHostAddress::LocalHost)) {
+ addresses.append(address.toString());
+ }
+ }
+
+ foreach (const QString& preferedIP, preferedIPAddress) {
+ foreach (const QString& address, addresses) {
+ if (address.startsWith(preferedIP)) {
+ return address;
+ }
+ }
+ }
+
+ return "";
+}
+
+bool ZeroconfService::registerService(bool server)
+{
+ bool result = true;
+
+ if (!m_ServiceRegistered) {
+ if (!m_zeroconfServer.listen()) {
+ QMessageBox::critical(0, tr("Zero configuration service"),
+ tr("Unable to start the zeroconf: %1.")
+ .arg(m_zeroconfServer.errorString()));
+ result = false;
+ }
+ else {
+ m_pZeroconfRegister = new ZeroconfRegister(this);
+ if (server) {
+ QString localIP = getLocalIPAddresses();
+ if (localIP.isEmpty()) {
+ QMessageBox::warning(m_pMainWindow, tr("Barrier"),
+ tr("Failed to get local IP address. "
+ "Please manually type in server address "
+ "on your clients"));
+ }
+ else {
+ m_pZeroconfRegister->registerService(
+ ZeroconfRecord(tr("%1").arg(localIP),
+ QLatin1String(m_ServerServiceName), QString()),
+ m_zeroconfServer.serverPort());
+ }
+ }
+ else {
+ m_pZeroconfRegister->registerService(
+ ZeroconfRecord(tr("%1").arg(m_pMainWindow->getScreenName()),
+ QLatin1String(m_ClientServiceName), QString()),
+ m_zeroconfServer.serverPort());
+ }
+
+ m_ServiceRegistered = true;
+ }
+ }
+
+ return result;
+}
diff --git a/src/gui/src/ZeroconfService.h b/src/gui/src/ZeroconfService.h
new file mode 100644
index 0000000..929cd05
--- /dev/null
+++ b/src/gui/src/ZeroconfService.h
@@ -0,0 +1,57 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include "ZeroconfServer.h"
+#include "ZeroconfRecord.h"
+
+#include <QtCore/QObject>
+
+typedef int32_t DNSServiceErrorType;
+
+class ZeroconfRegister;
+class ZeroconfBrowser;
+class MainWindow;
+
+class ZeroconfService : public QObject
+{
+ Q_OBJECT
+
+public:
+ ZeroconfService(MainWindow* mainWindow);
+ ~ZeroconfService();
+
+private slots:
+ void serverDetected(const QList<ZeroconfRecord>& list);
+ void clientDetected(const QList<ZeroconfRecord>& list);
+ void errorHandle(DNSServiceErrorType errorCode);
+
+private:
+ QString getLocalIPAddresses();
+ bool registerService(bool server);
+
+private:
+ MainWindow* m_pMainWindow;
+ ZeroconfServer m_zeroconfServer;
+ ZeroconfBrowser* m_pZeroconfBrowser;
+ ZeroconfRegister* m_pZeroconfRegister;
+ bool m_ServiceRegistered;
+
+ static const char* m_ServerServiceName;
+ static const char* m_ClientServiceName;
+};
diff --git a/src/gui/src/ZeroconfThread.cpp b/src/gui/src/ZeroconfThread.cpp
new file mode 100644
index 0000000..e9cea20
--- /dev/null
+++ b/src/gui/src/ZeroconfThread.cpp
@@ -0,0 +1,38 @@
+/*
+ * barrier -- 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 "ZeroconfThread.h"
+
+#include <QtNetwork>
+
+ZeroconfThread::ZeroconfThread(int socketDescriptor, QObject* parent) :
+ QThread(parent),
+ m_SocketDescriptor(socketDescriptor)
+{
+}
+
+void ZeroconfThread::run()
+{
+ QTcpSocket tcpSocket;
+ if (!tcpSocket.setSocketDescriptor(m_SocketDescriptor)) {
+ emit error(tcpSocket.error());
+ return;
+ }
+
+ tcpSocket.disconnectFromHost();
+ tcpSocket.waitForDisconnected();
+}
diff --git a/src/gui/src/ZeroconfThread.h b/src/gui/src/ZeroconfThread.h
new file mode 100644
index 0000000..aa488ad
--- /dev/null
+++ b/src/gui/src/ZeroconfThread.h
@@ -0,0 +1,38 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include <QThread>
+#include <QTcpSocket>
+
+class ZeroconfThread : public QThread
+{
+ Q_OBJECT
+
+public:
+ ZeroconfThread(int socketDescriptor, QObject* parent);
+
+ void run();
+
+signals:
+ void error(QTcpSocket::SocketError socketError);
+
+private:
+ int m_SocketDescriptor;
+ QString m_Text;
+};
diff --git a/src/gui/src/main.cpp b/src/gui/src/main.cpp
new file mode 100644
index 0000000..76a7d1a
--- /dev/null
+++ b/src/gui/src/main.cpp
@@ -0,0 +1,180 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2008 Volker Lanz (vl@fidra.de)
+ *
+ * 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/>.
+ */
+
+#define TRAY_RETRY_COUNT 10
+#define TRAY_RETRY_WAIT 2000
+
+#include "QBarrierApplication.h"
+#include "MainWindow.h"
+#include "AppConfig.h"
+#include "SetupWizard.h"
+#include "DisplayIsValid.h"
+
+#include <QtCore>
+#include <QtGui>
+#include <QSettings>
+#include <QMessageBox>
+
+#if defined(Q_OS_MAC)
+#include <Carbon/Carbon.h>
+#endif
+
+#ifdef Q_OS_DARWIN
+#include <cstdlib>
+#endif
+
+class QThreadImpl : public QThread
+{
+public:
+ static void msleep(unsigned long msecs)
+ {
+ QThread::msleep(msecs);
+ }
+};
+
+int waitForTray();
+
+#if defined(Q_OS_MAC)
+bool checkMacAssistiveDevices();
+#endif
+
+int main(int argc, char* argv[])
+{
+#ifdef WINAPI_XWINDOWS
+ // QApplication's constructor will call a fscking abort() if
+ // DISPLAY is bad. Let's check it first and handle it gracefully
+ if (!display_is_valid()) {
+ fprintf(stderr, "The Barrier GUI requires a display. Quitting...\n");
+ return 1;
+ }
+#endif
+#ifdef Q_OS_DARWIN
+ /* Workaround for QTBUG-40332 - "High ping when QNetworkAccessManager is instantiated" */
+ ::setenv ("QT_BEARER_POLL_TIMEOUT", "-1", 1);
+#endif
+ QCoreApplication::setOrganizationName("Debauchee");
+ QCoreApplication::setOrganizationDomain("github.com");
+ QCoreApplication::setApplicationName("Barrier");
+
+ QBarrierApplication app(argc, argv);
+
+#if defined(Q_OS_MAC)
+
+ if (app.applicationDirPath().startsWith("/Volumes/")) {
+ QMessageBox::information(
+ NULL, "Barrier",
+ "Please drag Barrier to the Applications folder, and open it from there.");
+ return 1;
+ }
+
+ if (!checkMacAssistiveDevices())
+ {
+ return 1;
+ }
+#endif
+
+ if (!waitForTray())
+ {
+ return -1;
+ }
+
+ QApplication::setQuitOnLastWindowClosed(false);
+
+ QSettings settings;
+ AppConfig appConfig (&settings);
+
+ app.switchTranslator(appConfig.language());
+
+ MainWindow mainWindow(settings, appConfig);
+ SetupWizard setupWizard(mainWindow, true);
+
+ if (appConfig.wizardShouldRun())
+ {
+ setupWizard.show();
+ }
+ else
+ {
+ mainWindow.open();
+ }
+
+ return app.exec();
+}
+
+int waitForTray()
+{
+ // on linux, the system tray may not be available immediately after logging in,
+ // so keep retrying but give up after a short time.
+ int trayAttempts = 0;
+ while (true)
+ {
+ if (QSystemTrayIcon::isSystemTrayAvailable())
+ {
+ break;
+ }
+
+ if (++trayAttempts > TRAY_RETRY_COUNT)
+ {
+ QMessageBox::critical(NULL, "Barrier",
+ QObject::tr("System tray is unavailable, don't close your window."));
+ return true;
+ }
+
+ QThreadImpl::msleep(TRAY_RETRY_WAIT);
+ }
+ return true;
+}
+
+#if defined(Q_OS_MAC)
+bool checkMacAssistiveDevices()
+{
+#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1090 // mavericks
+
+ // new in mavericks, applications are trusted individually
+ // with use of the accessibility api. this call will show a
+ // prompt which can show the security/privacy/accessibility
+ // tab, with a list of allowed applications. barrier should
+ // show up there automatically, but will be unchecked.
+
+ if (AXIsProcessTrusted()) {
+ return true;
+ }
+
+ const void* keys[] = { kAXTrustedCheckOptionPrompt };
+ const void* trueValue[] = { kCFBooleanTrue };
+ CFDictionaryRef options = CFDictionaryCreate(NULL, keys, trueValue, 1, NULL, NULL);
+
+ bool result = AXIsProcessTrustedWithOptions(options);
+ CFRelease(options);
+ return result;
+
+#else
+
+ // now deprecated in mavericks.
+ bool result = AXAPIEnabled();
+ if (!result) {
+ QMessageBox::information(
+ NULL, "Barrier",
+ "Please enable access to assistive devices "
+ "System Preferences -> Security & Privacy -> "
+ "Privacy -> Accessibility, then re-open Barrier.");
+ }
+ return result;
+
+#endif
+}
+#endif
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
new file mode 100644
index 0000000..70a0629
--- /dev/null
+++ b/src/lib/CMakeLists.txt
@@ -0,0 +1,27 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+add_subdirectory(arch)
+add_subdirectory(base)
+add_subdirectory(client)
+add_subdirectory(common)
+add_subdirectory(io)
+add_subdirectory(ipc)
+add_subdirectory(mt)
+add_subdirectory(net)
+add_subdirectory(platform)
+add_subdirectory(server)
+add_subdirectory(barrier)
diff --git a/src/lib/arch/Arch.cpp b/src/lib/arch/Arch.cpp
new file mode 100644
index 0000000..0a3b3e5
--- /dev/null
+++ b/src/lib/arch/Arch.cpp
@@ -0,0 +1,60 @@
+/*
+ * barrier -- 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 "arch/Arch.h"
+
+//
+// Arch
+//
+
+Arch* Arch::s_instance = NULL;
+
+Arch::Arch()
+{
+ assert(s_instance == NULL);
+ s_instance = this;
+}
+
+Arch::Arch(Arch* arch)
+{
+ s_instance = arch;
+}
+
+Arch::~Arch()
+{
+#if SYSAPI_WIN32
+ ArchMiscWindows::cleanup();
+#endif
+}
+
+void
+Arch::init()
+{
+ ARCH_NETWORK::init();
+#if SYSAPI_WIN32
+ ARCH_TASKBAR::init();
+ ArchMiscWindows::init();
+#endif
+}
+
+Arch*
+Arch::getInstance()
+{
+ assert(s_instance != NULL);
+ return s_instance;
+}
diff --git a/src/lib/arch/Arch.h b/src/lib/arch/Arch.h
new file mode 100644
index 0000000..42a73c2
--- /dev/null
+++ b/src/lib/arch/Arch.h
@@ -0,0 +1,144 @@
+/*
+ * barrier -- 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/>.
+ */
+
+// TODO: consider whether or not to use either encapsulation (as below)
+// or inheritance (as it is now) for the ARCH stuff.
+//
+// case for encapsulation:
+// pros:
+// - compiler errors for missing pv implementations are not absolutely bonkers.
+// - function names don't have to be so verbose.
+// - easier to understand and debug.
+// - ctors in IArch implementations can call other implementations.
+// cons:
+// - slightly more code for calls to ARCH.
+// - you'll have to modify each ARCH call.
+//
+// also, we may want to consider making each encapsulated
+// class lazy-loaded so that apps like the daemon don't load
+// stuff when they don't need it.
+
+#pragma once
+
+#include "common/common.h"
+
+#if SYSAPI_WIN32
+# include "arch/win32/ArchConsoleWindows.h"
+# include "arch/win32/ArchDaemonWindows.h"
+# include "arch/win32/ArchFileWindows.h"
+# include "arch/win32/ArchLogWindows.h"
+# include "arch/win32/ArchMiscWindows.h"
+# include "arch/win32/ArchMultithreadWindows.h"
+# include "arch/win32/ArchNetworkWinsock.h"
+# include "arch/win32/ArchSleepWindows.h"
+# include "arch/win32/ArchStringWindows.h"
+# include "arch/win32/ArchSystemWindows.h"
+# include "arch/win32/ArchTaskBarWindows.h"
+# include "arch/win32/ArchTimeWindows.h"
+# include "arch/win32/ArchInternetWindows.h"
+#elif SYSAPI_UNIX
+# include "arch/unix/ArchConsoleUnix.h"
+# include "arch/unix/ArchDaemonUnix.h"
+# include "arch/unix/ArchFileUnix.h"
+# include "arch/unix/ArchLogUnix.h"
+# if HAVE_PTHREAD
+# include "arch/unix/ArchMultithreadPosix.h"
+# endif
+# include "arch/unix/ArchNetworkBSD.h"
+# include "arch/unix/ArchSleepUnix.h"
+# include "arch/unix/ArchStringUnix.h"
+# include "arch/unix/ArchSystemUnix.h"
+# include "arch/unix/ArchTaskBarXWindows.h"
+# include "arch/unix/ArchTimeUnix.h"
+# include "arch/unix/ArchInternetUnix.h"
+#endif
+
+/*!
+\def ARCH
+This macro evaluates to the singleton Arch object.
+*/
+#define ARCH (Arch::getInstance())
+
+//! Delegating implementation of architecture dependent interfaces
+/*!
+This class is a centralized interface to all architecture dependent
+interface implementations (except miscellaneous functions). It
+instantiates an implementation of each interface and delegates calls
+to each method to those implementations. Clients should use the
+\c ARCH macro to access this object. Clients must also instantiate
+exactly one of these objects before attempting to call any method,
+typically at the beginning of \c main().
+*/
+class Arch : public ARCH_CONSOLE,
+ public ARCH_DAEMON,
+ public ARCH_FILE,
+ public ARCH_LOG,
+ public ARCH_MULTITHREAD,
+ public ARCH_NETWORK,
+ public ARCH_SLEEP,
+ public ARCH_STRING,
+ public ARCH_SYSTEM,
+ public ARCH_TASKBAR,
+ public ARCH_TIME {
+public:
+ Arch();
+ Arch(Arch* arch);
+ virtual ~Arch();
+
+ //! Call init on other arch classes.
+ /*!
+ Some arch classes depend on others to exist first. When init is called
+ these clases will have ARCH available for use.
+ */
+ virtual void init();
+
+ //
+ // accessors
+ //
+
+ //! Return the singleton instance
+ /*!
+ The client must have instantiated exactly once Arch object before
+ calling this function.
+ */
+ static Arch* getInstance();
+
+ static void setInstance(Arch* s) { s_instance = s; }
+
+ ARCH_INTERNET& internet() const { return (ARCH_INTERNET&)m_internet; }
+
+private:
+ static Arch* s_instance;
+ ARCH_INTERNET m_internet;
+};
+
+//! Convenience object to lock/unlock an arch mutex
+class ArchMutexLock {
+public:
+ ArchMutexLock(ArchMutex mutex) : m_mutex(mutex)
+ {
+ ARCH->lockMutex(m_mutex);
+ }
+ ~ArchMutexLock()
+ {
+ ARCH->unlockMutex(m_mutex);
+ }
+
+private:
+ ArchMutex m_mutex;
+};
diff --git a/src/lib/arch/ArchConsoleStd.cpp b/src/lib/arch/ArchConsoleStd.cpp
new file mode 100644
index 0000000..f7f7691
--- /dev/null
+++ b/src/lib/arch/ArchConsoleStd.cpp
@@ -0,0 +1,33 @@
+/*
+ * barrier -- 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 "arch/ArchConsoleStd.h"
+#include "base/Log.h"
+
+#include <iostream>
+
+void
+ArchConsoleStd::writeConsole(ELevel level, const char* str)
+{
+ if ((level >= kFATAL) && (level <= kWARNING))
+ std::cerr << str << std::endl;
+ else
+ std::cout << str << std::endl;
+
+ std::cout.flush();
+} \ No newline at end of file
diff --git a/src/lib/arch/ArchConsoleStd.h b/src/lib/arch/ArchConsoleStd.h
new file mode 100644
index 0000000..8560fad
--- /dev/null
+++ b/src/lib/arch/ArchConsoleStd.h
@@ -0,0 +1,34 @@
+/*
+ * barrier -- 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 "arch/IArchConsole.h"
+
+//! Cross platform implementation of IArchConsole
+class ArchConsoleStd : public IArchConsole {
+public:
+ ArchConsoleStd() { }
+ virtual ~ArchConsoleStd() { }
+
+ // IArchConsole overrides
+ virtual void openConsole(const char* title) { }
+ virtual void closeConsole() { }
+ virtual void showConsole(bool) { }
+ virtual void writeConsole(ELevel level, const char*);
+};
diff --git a/src/lib/arch/ArchDaemonNone.cpp b/src/lib/arch/ArchDaemonNone.cpp
new file mode 100644
index 0000000..1222549
--- /dev/null
+++ b/src/lib/arch/ArchDaemonNone.cpp
@@ -0,0 +1,85 @@
+/*
+ * barrier -- 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 "arch/ArchDaemonNone.h"
+
+//
+// ArchDaemonNone
+//
+
+ArchDaemonNone::ArchDaemonNone()
+{
+ // do nothing
+}
+
+ArchDaemonNone::~ArchDaemonNone()
+{
+ // do nothing
+}
+
+void
+ArchDaemonNone::installDaemon(const char*,
+ const char*,
+ const char*,
+ const char*,
+ const char*)
+{
+ // do nothing
+}
+
+void
+ArchDaemonNone::uninstallDaemon(const char*)
+{
+ // do nothing
+}
+
+int
+ArchDaemonNone::daemonize(const char* name, DaemonFunc func)
+{
+ // simply forward the call to func. obviously, this doesn't
+ // do any daemonizing.
+ return func(1, &name);
+}
+
+bool
+ArchDaemonNone::canInstallDaemon(const char*)
+{
+ return false;
+}
+
+bool
+ArchDaemonNone::isDaemonInstalled(const char*)
+{
+ return false;
+}
+
+void
+ArchDaemonNone::installDaemon()
+{
+}
+
+void
+ArchDaemonNone::uninstallDaemon()
+{
+}
+
+std::string
+ArchDaemonNone::commandLine() const
+{
+ return "";
+}
diff --git a/src/lib/arch/ArchDaemonNone.h b/src/lib/arch/ArchDaemonNone.h
new file mode 100644
index 0000000..fd59758
--- /dev/null
+++ b/src/lib/arch/ArchDaemonNone.h
@@ -0,0 +1,50 @@
+/*
+ * barrier -- 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 "arch/IArchDaemon.h"
+
+#define ARCH_DAEMON ArchDaemonNone
+
+//! Dummy implementation of IArchDaemon
+/*!
+This class implements IArchDaemon for a platform that does not have
+daemons. The install and uninstall functions do nothing, the query
+functions return false, and \c daemonize() simply calls the passed
+function and returns its result.
+*/
+class ArchDaemonNone : public IArchDaemon {
+public:
+ ArchDaemonNone();
+ virtual ~ArchDaemonNone();
+
+ // IArchDaemon overrides
+ virtual void installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies);
+ virtual void uninstallDaemon(const char* name);
+ virtual int daemonize(const char* name, DaemonFunc func);
+ virtual bool canInstallDaemon(const char* name);
+ virtual bool isDaemonInstalled(const char* name);
+ virtual void installDaemon();
+ virtual void uninstallDaemon();
+ virtual std::string commandLine() const;
+};
diff --git a/src/lib/arch/CMakeLists.txt b/src/lib/arch/CMakeLists.txt
new file mode 100644
index 0000000..113cdd9
--- /dev/null
+++ b/src/lib/arch/CMakeLists.txt
@@ -0,0 +1,47 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+# arch
+if (WIN32)
+ file(GLOB arch_headers "win32/*.h")
+ file(GLOB arch_sources "win32/*.cpp")
+elseif (UNIX)
+ file(GLOB arch_headers "unix/*.h")
+ file(GLOB arch_sources "unix/*.cpp")
+endif()
+
+list(APPEND sources ${arch_sources})
+list(APPEND headers ${arch_headers})
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(arch STATIC ${sources})
+
+if (UNIX)
+ target_link_libraries(arch ${libs})
+ if (NOT CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+ target_link_libraries(arch dl)
+ endif()
+endif()
diff --git a/src/lib/arch/IArchConsole.h b/src/lib/arch/IArchConsole.h
new file mode 100644
index 0000000..d115c50
--- /dev/null
+++ b/src/lib/arch/IArchConsole.h
@@ -0,0 +1,66 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+#include "base/ELevel.h"
+
+//! Interface for architecture dependent console output
+/*!
+This interface defines the console operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchConsole : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Open the console
+ /*!
+ Opens the console for writing. The console is opened automatically
+ on the first write so calling this method is optional. Uses \c title
+ for the console's title if appropriate for the architecture. Calling
+ this method on an already open console must have no effect.
+ */
+ virtual void openConsole(const char* title) = 0;
+
+ //! Close the console
+ /*!
+ Close the console. Calling this method on an already closed console
+ must have no effect.
+ */
+ virtual void closeConsole() = 0;
+
+ //! Show the console
+ /*!
+ Causes the console to become visible. This generally only makes sense
+ for a console in a graphical user interface. Other implementations
+ will do nothing. Iff \p showIfEmpty is \c false then the implementation
+ may optionally only show the console if it's not empty.
+ */
+ virtual void showConsole(bool showIfEmpty) = 0;
+
+ //! Write to the console
+ /*!
+ Writes the given string to the console, opening it if necessary.
+ */
+ virtual void writeConsole(ELevel, const char*) = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchDaemon.h b/src/lib/arch/IArchDaemon.h
new file mode 100644
index 0000000..a4983d3
--- /dev/null
+++ b/src/lib/arch/IArchDaemon.h
@@ -0,0 +1,128 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+#include "base/String.h"
+
+//! Interface for architecture dependent daemonizing
+/*!
+This interface defines the operations required by barrier for installing
+uninstalling daeamons and daemonizing a process. Each architecture must
+implement this interface.
+*/
+class IArchDaemon : public IInterface {
+public:
+ typedef int (*DaemonFunc)(int argc, const char** argv);
+
+ //! @name manipulators
+ //@{
+
+ //! Install daemon
+ /*!
+ Install a daemon. \c name is the name of the daemon passed to the
+ system and \c description is a short human readable description of
+ the daemon. \c pathname is the path to the daemon executable.
+ \c commandLine should \b not include the name of program as the
+ first argument. If \c allUsers is true then the daemon will be
+ installed to start at boot time, otherwise it will be installed to
+ start when the current user logs in. If \p dependencies is not NULL
+ then it's a concatenation of NUL terminated other daemon names
+ followed by a NUL; the daemon will be configured to startup after
+ the listed daemons. Throws an \c XArchDaemon exception on failure.
+ */
+ virtual void installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies) = 0;
+
+ //! Uninstall daemon
+ /*!
+ Uninstall a daemon. Throws an \c XArchDaemon on failure.
+ */
+ virtual void uninstallDaemon(const char* name) = 0;
+
+ //! Install daemon
+ /*!
+ Installs the default daemon.
+ */
+ virtual void installDaemon() = 0;
+
+ //! Uninstall daemon
+ /*!
+ Uninstalls the default daemon.
+ */
+ virtual void uninstallDaemon() = 0;
+
+ //! Daemonize the process
+ /*!
+ Daemonize. Throw XArchDaemonFailed on error. \c name is the name
+ of the daemon. Once daemonized, \c func is invoked and daemonize
+ returns when and what it does.
+
+ Exactly what happens when daemonizing depends on the platform.
+ <ul>
+ <li>unix:
+ Detaches from terminal. \c func gets passed one argument, the
+ name passed to daemonize().
+ <li>win32:
+ Becomes a service. Argument 0 is the name of the service
+ and the rest are the arguments passed to StartService().
+ \c func is only called when the service is actually started.
+ \c func must call \c ArchMiscWindows::runDaemon() to finally
+ becoming a service. The \c runFunc function passed to \c runDaemon()
+ must call \c ArchMiscWindows::daemonRunning(true) when it
+ enters the main loop (i.e. after initialization) and
+ \c ArchMiscWindows::daemonRunning(false) when it leaves
+ the main loop. The \c stopFunc function passed to \c runDaemon()
+ is called when the daemon must exit the main loop and it must cause
+ \c runFunc to return. \c func should return what \c runDaemon()
+ returns. \c func or \c runFunc can call
+ \c ArchMiscWindows::daemonFailed() to indicate startup failure.
+ </ul>
+ */
+ virtual int daemonize(const char* name, DaemonFunc func) = 0;
+
+ //! Check if user has permission to install the daemon
+ /*!
+ Returns true iff the caller has permission to install or
+ uninstall the daemon. Note that even if this method returns
+ true it's possible that installing/uninstalling the service
+ may still fail. This method ignores whether or not the
+ service is already installed.
+ */
+ virtual bool canInstallDaemon(const char* name) = 0;
+
+ //! Check if the daemon is installed
+ /*!
+ Returns true iff the daemon is installed.
+ */
+ virtual bool isDaemonInstalled(const char* name) = 0;
+
+ //@}
+
+ //! Get the command line
+ /*!
+ Gets the command line with which the application was started.
+ */
+ virtual std::string commandLine() const = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchFile.h b/src/lib/arch/IArchFile.h
new file mode 100644
index 0000000..5fdd288
--- /dev/null
+++ b/src/lib/arch/IArchFile.h
@@ -0,0 +1,105 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+#include "common/stdstring.h"
+#include "base/String.h"
+
+//! Interface for architecture dependent file system operations
+/*!
+This interface defines the file system operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchFile : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Extract base name
+ /*!
+ Find the base name in the given \c pathname.
+ */
+ virtual const char* getBasename(const char* pathname) = 0;
+
+ //! Get user's home directory
+ /*!
+ Returns the user's home directory. Returns the empty string if
+ this cannot be determined.
+ */
+ virtual std::string getUserDirectory() = 0;
+
+ //! Get system directory
+ /*!
+ Returns the ussystem configuration file directory.
+ */
+ virtual std::string getSystemDirectory() = 0;
+
+ //! Get installed directory
+ /*!
+ Returns the directory in which Barrier is installed.
+ */
+ virtual std::string getInstalledDirectory() = 0;
+
+ //! Get log directory
+ /*!
+ Returns the log file directory.
+ */
+ virtual std::string getLogDirectory() = 0;
+
+ //! Get plugins directory
+ /*!
+ Returns the plugin files directory. If no plugin directory is set,
+ this will return the plugin folder within the user's profile.
+ */
+ virtual std::string getPluginDirectory() = 0;
+
+ //! Get user's profile directory
+ /*!
+ Returns the user's profile directory. If no profile directory is set,
+ this will return the user's profile according to the operating system,
+ which will depend on which user launched the program.
+ */
+ virtual std::string getProfileDirectory() = 0;
+
+ //! Concatenate path components
+ /*!
+ Concatenate pathname components with a directory separator
+ between them. This should not check if the resulting path
+ is longer than allowed by the system; we'll rely on the
+ system calls to tell us that.
+ */
+ virtual std::string concatPath(
+ const std::string& prefix,
+ const std::string& suffix) = 0;
+
+ //@}
+ //! Set the user's profile directory
+ /*
+ Returns the user's profile directory.
+ */
+ virtual void setProfileDirectory(const String& s) = 0;
+
+ //@}
+ //! Set the user's plugin directory
+ /*
+ Returns the user's plugin directory.
+ */
+ virtual void setPluginDirectory(const String& s) = 0;
+};
diff --git a/src/lib/arch/IArchLog.h b/src/lib/arch/IArchLog.h
new file mode 100644
index 0000000..165b1df
--- /dev/null
+++ b/src/lib/arch/IArchLog.h
@@ -0,0 +1,63 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+#include "base/ELevel.h"
+
+//! Interface for architecture dependent logging
+/*!
+This interface defines the logging operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchLog : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Open the log
+ /*!
+ Opens the log for writing. The log must be opened before being
+ written to.
+ */
+ virtual void openLog(const char* name) = 0;
+
+ //! Close the log
+ /*!
+ Close the log.
+ */
+ virtual void closeLog() = 0;
+
+ //! Show the log
+ /*!
+ Causes the log to become visible. This generally only makes sense
+ for a log in a graphical user interface. Other implementations
+ will do nothing. Iff \p showIfEmpty is \c false then the implementation
+ may optionally only show the log if it's not empty.
+ */
+ virtual void showLog(bool showIfEmpty) = 0;
+
+ //! Write to the log
+ /*!
+ Writes the given string to the log with the given level.
+ */
+ virtual void writeLog(ELevel, const char*) = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchMultithread.h b/src/lib/arch/IArchMultithread.h
new file mode 100644
index 0000000..e8d358b
--- /dev/null
+++ b/src/lib/arch/IArchMultithread.h
@@ -0,0 +1,273 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+
+/*!
+\class ArchCondImpl
+\brief Internal condition variable data.
+An architecture dependent type holding the necessary data for a
+condition variable.
+*/
+class ArchCondImpl;
+
+/*!
+\var ArchCond
+\brief Opaque condition variable type.
+An opaque type representing a condition variable.
+*/
+typedef ArchCondImpl* ArchCond;
+
+/*!
+\class ArchMutexImpl
+\brief Internal mutex data.
+An architecture dependent type holding the necessary data for a mutex.
+*/
+class ArchMutexImpl;
+
+/*!
+\var ArchMutex
+\brief Opaque mutex type.
+An opaque type representing a mutex.
+*/
+typedef ArchMutexImpl* ArchMutex;
+
+/*!
+\class ArchThreadImpl
+\brief Internal thread data.
+An architecture dependent type holding the necessary data for a thread.
+*/
+class ArchThreadImpl;
+
+/*!
+\var ArchThread
+\brief Opaque thread type.
+An opaque type representing a thread.
+*/
+typedef ArchThreadImpl* ArchThread;
+
+//! Interface for architecture dependent multithreading
+/*!
+This interface defines the multithreading operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchMultithread : public IInterface {
+public:
+ //! Type of thread entry point
+ typedef void* (*ThreadFunc)(void*);
+ //! Type of thread identifier
+ typedef unsigned int ThreadID;
+ //! Types of signals
+ /*!
+ Not all platforms support all signals. Unsupported signals are
+ ignored.
+ */
+ enum ESignal {
+ kINTERRUPT, //!< Interrupt (e.g. Ctrl+C)
+ kTERMINATE, //!< Terminate (e.g. Ctrl+Break)
+ kHANGUP, //!< Hangup (SIGHUP)
+ kUSER, //!< User (SIGUSR2)
+ kNUM_SIGNALS
+ };
+ //! Type of signal handler function
+ typedef void (*SignalFunc)(ESignal, void* userData);
+
+ //! @name manipulators
+ //@{
+
+ //
+ // condition variable methods
+ //
+
+ //! Create a condition variable
+ /*!
+ The condition variable is an opaque data type.
+ */
+ virtual ArchCond newCondVar() = 0;
+
+ //! Destroy a condition variable
+ virtual void closeCondVar(ArchCond) = 0;
+
+ //! Signal a condition variable
+ /*!
+ Signalling a condition variable releases one waiting thread.
+ */
+ virtual void signalCondVar(ArchCond) = 0;
+
+ //! Broadcast a condition variable
+ /*!
+ Broadcasting a condition variable releases all waiting threads.
+ */
+ virtual void broadcastCondVar(ArchCond) = 0;
+
+ //! Wait on a condition variable
+ /*!
+ Wait on a conditation variable for up to \c timeout seconds.
+ If \c timeout is < 0 then there is no timeout. The mutex must
+ be locked when this method is called. The mutex is unlocked
+ during the wait and locked again before returning. Returns
+ true if the condition variable was signalled and false on
+ timeout.
+
+ (Cancellation point)
+ */
+ virtual bool waitCondVar(ArchCond, ArchMutex, double timeout) = 0;
+
+ //
+ // mutex methods
+ //
+
+ //! Create a recursive mutex
+ /*!
+ Creates a recursive mutex. A thread may lock a recursive mutex
+ when it already holds a lock on that mutex. The mutex is an
+ opaque data type.
+ */
+ virtual ArchMutex newMutex() = 0;
+
+ //! Destroy a mutex
+ virtual void closeMutex(ArchMutex) = 0;
+
+ //! Lock a mutex
+ virtual void lockMutex(ArchMutex) = 0;
+
+ //! Unlock a mutex
+ virtual void unlockMutex(ArchMutex) = 0;
+
+ //
+ // thread methods
+ //
+
+ //! Start a new thread
+ /*!
+ Creates and starts a new thread, using \c func as the entry point
+ and passing it \c userData. The thread is an opaque data type.
+ */
+ virtual ArchThread newThread(ThreadFunc func, void* userData) = 0;
+
+ //! Get a reference to the calling thread
+ /*!
+ Returns a thread representing the current (i.e. calling) thread.
+ */
+ virtual ArchThread newCurrentThread() = 0;
+
+ //! Copy a thread object
+ /*!
+ Returns a reference to to thread referred to by \c thread.
+ */
+ virtual ArchThread copyThread(ArchThread thread) = 0;
+
+ //! Release a thread reference
+ /*!
+ Deletes the given thread object. This does not destroy the thread
+ the object referred to, even if there are no remaining references.
+ Use cancelThread() and waitThread() to stop a thread and wait for
+ it to exit.
+ */
+ virtual void closeThread(ArchThread) = 0;
+
+ //! Force a thread to exit
+ /*!
+ Causes \c thread to exit when it next calls a cancellation point.
+ A thread avoids cancellation as long as it nevers calls a
+ cancellation point. Once it begins the cancellation process it
+ must always let cancellation go to completion but may take as
+ long as necessary to clean up.
+ */
+ virtual void cancelThread(ArchThread thread) = 0;
+
+ //! Change thread priority
+ /*!
+ Changes the priority of \c thread by \c n. If \c n is positive
+ the thread has a lower priority and if negative a higher priority.
+ Some architectures may not support either or both directions.
+ */
+ virtual void setPriorityOfThread(ArchThread, int n) = 0;
+
+ //! Cancellation point
+ /*!
+ This method does nothing but is a cancellation point. Clients
+ can make their own functions cancellation points by calling this
+ method at appropriate times.
+
+ (Cancellation point)
+ */
+ virtual void testCancelThread() = 0;
+
+ //! Wait for a thread to exit
+ /*!
+ Waits for up to \c timeout seconds for \c thread to exit (normally
+ or by cancellation). Waits forever if \c timeout < 0. Returns
+ true if the thread exited, false otherwise. Waiting on the current
+ thread returns immediately with false.
+
+ (Cancellation point)
+ */
+ virtual bool wait(ArchThread thread, double timeout) = 0;
+
+ //! Compare threads
+ /*!
+ Returns true iff two thread objects refer to the same thread.
+ Note that comparing thread objects directly is meaningless.
+ */
+ virtual bool isSameThread(ArchThread, ArchThread) = 0;
+
+ //! Test if thread exited
+ /*!
+ Returns true iff \c thread has exited.
+ */
+ virtual bool isExitedThread(ArchThread thread) = 0;
+
+ //! Returns the exit code of a thread
+ /*!
+ Waits indefinitely for \c thread to exit (if it hasn't yet) then
+ returns the thread's exit code.
+
+ (Cancellation point)
+ */
+ virtual void* getResultOfThread(ArchThread thread) = 0;
+
+ //! Returns an ID for a thread
+ /*!
+ Returns some ID number for \c thread. This is for logging purposes.
+ All thread objects referring to the same thread return the same ID.
+ However, clients should us isSameThread() to compare thread objects
+ instead of comparing IDs.
+ */
+ virtual ThreadID getIDOfThread(ArchThread thread) = 0;
+
+ //! Set the interrupt handler
+ /*!
+ Sets the function to call on receipt of an external interrupt.
+ By default and when \p func is NULL, the main thread is cancelled.
+ */
+ virtual void setSignalHandler(ESignal, SignalFunc func,
+ void* userData) = 0;
+
+ //! Invoke the signal handler
+ /*!
+ Invokes the signal handler for \p signal, if any. If no handler
+ cancels the main thread for \c kINTERRUPT and \c kTERMINATE and
+ ignores the call otherwise.
+ */
+ virtual void raiseSignal(ESignal signal) = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchNetwork.h b/src/lib/arch/IArchNetwork.h
new file mode 100644
index 0000000..b859506
--- /dev/null
+++ b/src/lib/arch/IArchNetwork.h
@@ -0,0 +1,283 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+#include "common/stdstring.h"
+
+class ArchThreadImpl;
+typedef ArchThreadImpl* ArchThread;
+
+/*!
+\class ArchSocketImpl
+\brief Internal socket data.
+An architecture dependent type holding the necessary data for a socket.
+*/
+class ArchSocketImpl;
+
+/*!
+\var ArchSocket
+\brief Opaque socket type.
+An opaque type representing a socket.
+*/
+typedef ArchSocketImpl* ArchSocket;
+
+/*!
+\class ArchNetAddressImpl
+\brief Internal network address data.
+An architecture dependent type holding the necessary data for a network
+address.
+*/
+class ArchNetAddressImpl;
+
+/*!
+\var ArchNetAddress
+\brief Opaque network address type.
+An opaque type representing a network address.
+*/
+typedef ArchNetAddressImpl* ArchNetAddress;
+
+//! Interface for architecture dependent networking
+/*!
+This interface defines the networking operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchNetwork : public IInterface {
+public:
+ //! Supported address families
+ enum EAddressFamily {
+ kUNKNOWN,
+ kINET,
+ kINET6,
+ };
+
+ //! Supported socket types
+ enum ESocketType {
+ kDGRAM,
+ kSTREAM
+ };
+
+ //! Events for \c poll()
+ /*!
+ Events for \c poll() are bitmasks and can be combined using the
+ bitwise operators.
+ */
+ enum {
+ kPOLLIN = 1, //!< Socket is readable
+ kPOLLOUT = 2, //!< Socket is writable
+ kPOLLERR = 4, //!< The socket is in an error state
+ kPOLLNVAL = 8 //!< The socket is invalid
+ };
+
+ //! A socket query for \c poll()
+ class PollEntry {
+ public:
+ //! The socket to query
+ ArchSocket m_socket;
+
+ //! The events to query for
+ /*!
+ The events to query for can be any combination of kPOLLIN and
+ kPOLLOUT.
+ */
+ unsigned short m_events;
+
+ //! The result events
+ unsigned short m_revents;
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! Create a new socket
+ /*!
+ The socket is an opaque data type.
+ */
+ virtual ArchSocket newSocket(EAddressFamily, ESocketType) = 0;
+
+ //! Copy a socket object
+ /*!
+ Returns a reference to to socket referred to by \c s.
+ */
+ virtual ArchSocket copySocket(ArchSocket s) = 0;
+
+ //! Release a socket reference
+ /*!
+ Deletes the given socket object. This does not destroy the socket
+ the object referred to until there are no remaining references for
+ the socket.
+ */
+ virtual void closeSocket(ArchSocket s) = 0;
+
+ //! Close socket for further reads
+ /*!
+ Calling this disallows future reads on socket \c s.
+ */
+ virtual void closeSocketForRead(ArchSocket s) = 0;
+
+ //! Close socket for further writes
+ /*!
+ Calling this disallows future writes on socket \c s.
+ */
+ virtual void closeSocketForWrite(ArchSocket s) = 0;
+
+ //! Bind socket to address
+ /*!
+ Binds socket \c s to the address \c addr.
+ */
+ virtual void bindSocket(ArchSocket s, ArchNetAddress addr) = 0;
+
+ //! Listen for connections on socket
+ /*!
+ Causes the socket \c s to begin listening for incoming connections.
+ */
+ virtual void listenOnSocket(ArchSocket s) = 0;
+
+ //! Accept connection on socket
+ /*!
+ Accepts a connection on socket \c s, returning a new socket for the
+ connection and filling in \c addr with the address of the remote
+ end. \c addr may be NULL if the remote address isn't required.
+ The original socket \c s is unaffected and remains in the listening
+ state. The new socket shares most of the properties of \c s except
+ it's not in the listening state and it's connected. Returns NULL
+ if there are no pending connection requests.
+ */
+ virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress* addr) = 0;
+
+ //! Connect socket
+ /*!
+ Connects the socket \c s to the remote address \c addr. Returns
+ true if the connection succeed immediately, false if the connection
+ is in progress, and throws if the connection failed immediately.
+ If it returns false, \c pollSocket() can be used to wait on the
+ socket for writing to detect when the connection finally succeeds
+ or fails.
+ */
+ virtual bool connectSocket(ArchSocket s, ArchNetAddress addr) = 0;
+
+ //! Check socket state
+ /*!
+ Tests the state of \c num sockets for readability and/or writability.
+ Waits up to \c timeout seconds for some socket to become readable
+ and/or writable (or indefinitely if \c timeout < 0). Returns the
+ number of sockets that were readable (if readability was being
+ queried) or writable (if writablility was being queried) and sets
+ the \c m_revents members of the entries. \c kPOLLERR and \c kPOLLNVAL
+ are set in \c m_revents as appropriate. If a socket indicates
+ \c kPOLLERR then \c throwErrorOnSocket() can be used to determine
+ the type of error. Returns 0 immediately regardless of the \c timeout
+ if no valid sockets are selected for testing.
+
+ (Cancellation point)
+ */
+ virtual int pollSocket(PollEntry[], int num, double timeout) = 0;
+
+ //! Unblock thread in pollSocket()
+ /*!
+ Cause a thread that's in a pollSocket() call to return. This
+ call may return before the thread is unblocked. If the thread is
+ not in a pollSocket() call this call has no effect.
+ */
+ virtual void unblockPollSocket(ArchThread thread) = 0;
+
+ //! Read data from socket
+ /*!
+ Read up to \c len bytes from socket \c s in \c buf and return the
+ number of bytes read. The number of bytes can be less than \c len
+ if not enough data is available. Returns 0 if the remote end has
+ disconnected and/or there is no more queued received data.
+ */
+ virtual size_t readSocket(ArchSocket s, void* buf, size_t len) = 0;
+
+ //! Write data from socket
+ /*!
+ Write up to \c len bytes to socket \c s from \c buf and return the
+ number of bytes written. The number of bytes can be less than
+ \c len if the remote end disconnected or the internal buffers fill
+ up.
+ */
+ virtual size_t writeSocket(ArchSocket s,
+ const void* buf, size_t len) = 0;
+
+ //! Check error on socket
+ /*!
+ If the socket \c s is in an error state then throws an appropriate
+ XArchNetwork exception.
+ */
+ virtual void throwErrorOnSocket(ArchSocket s) = 0;
+
+ //! Turn Nagle algorithm on or off on socket
+ /*!
+ Set socket to send messages immediately (true) or to collect small
+ messages into one packet (false). Returns the previous state.
+ */
+ virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay) = 0;
+
+ //! Turn address reuse on or off on socket
+ /*!
+ Allows the address this socket is bound to to be reused while in the
+ TIME_WAIT state. Returns the previous state.
+ */
+ virtual bool setReuseAddrOnSocket(ArchSocket, bool reuse) = 0;
+
+ //! Return local host's name
+ virtual std::string getHostName() = 0;
+
+ //! Create an "any" network address
+ virtual ArchNetAddress newAnyAddr(EAddressFamily) = 0;
+
+ //! Copy a network address
+ virtual ArchNetAddress copyAddr(ArchNetAddress) = 0;
+
+ //! Convert a name to a network address
+ virtual ArchNetAddress nameToAddr(const std::string&) = 0;
+
+ //! Destroy a network address
+ virtual void closeAddr(ArchNetAddress) = 0;
+
+ //! Convert an address to a host name
+ virtual std::string addrToName(ArchNetAddress) = 0;
+
+ //! Convert an address to a string
+ virtual std::string addrToString(ArchNetAddress) = 0;
+
+ //! Get an address's family
+ virtual EAddressFamily getAddrFamily(ArchNetAddress) = 0;
+
+ //! Set the port of an address
+ virtual void setAddrPort(ArchNetAddress, int port) = 0;
+
+ //! Get the port of an address
+ virtual int getAddrPort(ArchNetAddress) = 0;
+
+ //! Test addresses for equality
+ virtual bool isEqualAddr(ArchNetAddress, ArchNetAddress) = 0;
+
+ //! Test for the "any" address
+ /*!
+ Returns true if \c addr is the "any" address. \c newAnyAddr()
+ returns an "any" address.
+ */
+ virtual bool isAnyAddr(ArchNetAddress addr) = 0;
+
+ //@}
+
+ virtual void init() = 0;
+};
diff --git a/src/lib/arch/IArchSleep.h b/src/lib/arch/IArchSleep.h
new file mode 100644
index 0000000..9999d0e
--- /dev/null
+++ b/src/lib/arch/IArchSleep.h
@@ -0,0 +1,44 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+
+//! Interface for architecture dependent sleeping
+/*!
+This interface defines the sleep operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchSleep : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Sleep
+ /*!
+ Blocks the calling thread for \c timeout seconds. If
+ \c timeout < 0.0 then the call returns immediately. If \c timeout
+ == 0.0 then the calling thread yields the CPU.
+
+ (cancellation point)
+ */
+ virtual void sleep(double timeout) = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchString.cpp b/src/lib/arch/IArchString.cpp
new file mode 100644
index 0000000..f618c12
--- /dev/null
+++ b/src/lib/arch/IArchString.cpp
@@ -0,0 +1,190 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 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 "arch/IArchString.h"
+#include "arch/Arch.h"
+#include "common/common.h"
+
+#include <climits>
+#include <cstring>
+#include <cstdlib>
+
+static ArchMutex s_mutex = NULL;
+
+//
+// use C library non-reentrant multibyte conversion with mutex
+//
+
+IArchString::~IArchString()
+{
+ if (s_mutex != NULL) {
+ ARCH->closeMutex(s_mutex);
+ s_mutex = NULL;
+ }
+}
+
+int
+IArchString::convStringWCToMB(char* dst,
+ const wchar_t* src, UInt32 n, bool* errors)
+{
+ ptrdiff_t len = 0;
+
+ bool dummyErrors;
+ if (errors == NULL) {
+ errors = &dummyErrors;
+ }
+
+ if (s_mutex == NULL) {
+ s_mutex = ARCH->newMutex();
+ }
+
+ ARCH->lockMutex(s_mutex);
+
+ if (dst == NULL) {
+ char dummy[MB_LEN_MAX];
+ for (const wchar_t* scan = src; n > 0; ++scan, --n) {
+ ptrdiff_t mblen = wctomb(dummy, *scan);
+ if (mblen == -1) {
+ *errors = true;
+ mblen = 1;
+ }
+ len += mblen;
+ }
+ ptrdiff_t mblen = wctomb(dummy, L'\0');
+ if (mblen != -1) {
+ len += mblen - 1;
+ }
+ }
+ else {
+ char* dst0 = dst;
+ for (const wchar_t* scan = src; n > 0; ++scan, --n) {
+ ptrdiff_t mblen = wctomb(dst, *scan);
+ if (mblen == -1) {
+ *errors = true;
+ *dst++ = '?';
+ }
+ else {
+ dst += mblen;
+ }
+ }
+ ptrdiff_t mblen = wctomb(dst, L'\0');
+ if (mblen != -1) {
+ // don't include nul terminator
+ dst += mblen - 1;
+ }
+ len = dst - dst0;
+ }
+ ARCH->unlockMutex(s_mutex);
+
+ return (int)len;
+}
+
+int
+IArchString::convStringMBToWC(wchar_t* dst,
+ const char* src, UInt32 n_param, bool* errors)
+{
+ ptrdiff_t n = (ptrdiff_t)n_param; // fix compiler warning
+ ptrdiff_t len = 0;
+ wchar_t dummy;
+
+ bool dummyErrors;
+ if (errors == NULL) {
+ errors = &dummyErrors;
+ }
+
+ if (s_mutex == NULL) {
+ s_mutex = ARCH->newMutex();
+ }
+
+ ARCH->lockMutex(s_mutex);
+
+ if (dst == NULL) {
+ for (const char* scan = src; n > 0; ) {
+ ptrdiff_t mblen = mbtowc(&dummy, scan, n);
+ switch (mblen) {
+ case -2:
+ // incomplete last character. convert to unknown character.
+ *errors = true;
+ len += 1;
+ n = 0;
+ break;
+
+ case -1:
+ // invalid character. count one unknown character and
+ // start at the next byte.
+ *errors = true;
+ len += 1;
+ scan += 1;
+ n -= 1;
+ break;
+
+ case 0:
+ len += 1;
+ scan += 1;
+ n -= 1;
+ break;
+
+ default:
+ // normal character
+ len += 1;
+ scan += mblen;
+ n -= mblen;
+ break;
+ }
+ }
+ }
+ else {
+ wchar_t* dst0 = dst;
+ for (const char* scan = src; n > 0; ++dst) {
+ ptrdiff_t mblen = mbtowc(dst, scan, n);
+ switch (mblen) {
+ case -2:
+ // incomplete character. convert to unknown character.
+ *errors = true;
+ *dst = (wchar_t)0xfffd;
+ n = 0;
+ break;
+
+ case -1:
+ // invalid character. count one unknown character and
+ // start at the next byte.
+ *errors = true;
+ *dst = (wchar_t)0xfffd;
+ scan += 1;
+ n -= 1;
+ break;
+
+ case 0:
+ *dst = (wchar_t)0x0000;
+ scan += 1;
+ n -= 1;
+ break;
+
+ default:
+ // normal character
+ scan += mblen;
+ n -= mblen;
+ break;
+ }
+ }
+ len = dst - dst0;
+ }
+ ARCH->unlockMutex(s_mutex);
+
+ return (int)len;
+}
diff --git a/src/lib/arch/IArchString.h b/src/lib/arch/IArchString.h
new file mode 100644
index 0000000..ea10b65
--- /dev/null
+++ b/src/lib/arch/IArchString.h
@@ -0,0 +1,72 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+#include "common/basic_types.h"
+
+#include <stdarg.h>
+
+//! Interface for architecture dependent string operations
+/*!
+This interface defines the string operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchString : public IInterface {
+public:
+ virtual ~IArchString();
+
+ //! Wide character encodings
+ /*!
+ The known wide character encodings
+ */
+ enum EWideCharEncoding {
+ kUCS2, //!< The UCS-2 encoding
+ kUCS4, //!< The UCS-4 encoding
+ kUTF16, //!< The UTF-16 encoding
+ kUTF32 //!< The UTF-32 encoding
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! printf() to limited size buffer with va_list
+ /*!
+ This method is equivalent to vsprintf() except it will not write
+ more than \c n bytes to the buffer, returning -1 if the output
+ was truncated and the number of bytes written not including the
+ trailing NUL otherwise.
+ */
+ virtual int vsnprintf(char* str,
+ int size, const char* fmt, va_list ap);
+
+ //! Convert multibyte string to wide character string
+ virtual int convStringMBToWC(wchar_t*,
+ const char*, UInt32 n, bool* errors);
+
+ //! Convert wide character string to multibyte string
+ virtual int convStringWCToMB(char*,
+ const wchar_t*, UInt32 n, bool* errors);
+
+ //! Return the architecture's native wide character encoding
+ virtual EWideCharEncoding
+ getWideCharEncoding() = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/IArchSystem.h b/src/lib/arch/IArchSystem.h
new file mode 100644
index 0000000..9446505
--- /dev/null
+++ b/src/lib/arch/IArchSystem.h
@@ -0,0 +1,66 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "common/IInterface.h"
+#include "common/stdstring.h"
+
+//! Interface for architecture dependent system queries
+/*!
+This interface defines operations for querying system info.
+*/
+class IArchSystem : public IInterface {
+public:
+ //! @name accessors
+ //@{
+
+ //! Identify the OS
+ /*!
+ Returns a string identifying the operating system.
+ */
+ virtual std::string getOSName() const = 0;
+
+ //! Identify the platform
+ /*!
+ Returns a string identifying the platform this OS is running on.
+ */
+ virtual std::string getPlatformName() const = 0;
+ //@}
+
+ //! Get a Barrier setting
+ /*!
+ Reads a Barrier setting from the system.
+ */
+ virtual std::string setting(const std::string& valueName) const = 0;
+ //@}
+
+ //! Set a Barrier setting
+ /*!
+ Writes a Barrier setting from the system.
+ */
+ virtual void setting(const std::string& valueName, const std::string& valueString) const = 0;
+ //@}
+
+ //! Get the pathnames of the libraries used by Barrier
+ /*
+ Returns a string containing the full path names of all loaded libraries at the point it is called.
+ */
+ virtual std::string getLibsUsed(void) const = 0;
+ //@}
+};
diff --git a/src/lib/arch/IArchTaskBar.h b/src/lib/arch/IArchTaskBar.h
new file mode 100644
index 0000000..85a32d8
--- /dev/null
+++ b/src/lib/arch/IArchTaskBar.h
@@ -0,0 +1,63 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "common/IInterface.h"
+
+class IArchTaskBarReceiver;
+
+//! Interface for architecture dependent task bar control
+/*!
+This interface defines the task bar icon operations required
+by barrier. Each architecture must implement this interface
+though each operation can be a no-op.
+*/
+class IArchTaskBar : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Add a receiver
+ /*!
+ Add a receiver object to be notified of user and application
+ events. This should be called before other methods. When
+ the receiver is added to the task bar, its icon appears on
+ the task bar.
+ */
+ virtual void addReceiver(IArchTaskBarReceiver*) = 0;
+
+ //! Remove a receiver
+ /*!
+ Remove a receiver object from the task bar. This removes the
+ icon from the task bar.
+ */
+ virtual void removeReceiver(IArchTaskBarReceiver*) = 0;
+
+ //! Update a receiver
+ /*!
+ Updates the display of the receiver on the task bar. This
+ should be called when the receiver appearance may have changed
+ (e.g. it's icon or tool tip has changed).
+ */
+ virtual void updateReceiver(IArchTaskBarReceiver*) = 0;
+
+ //@}
+
+ virtual void init() = 0;
+};
diff --git a/src/lib/arch/IArchTaskBarReceiver.h b/src/lib/arch/IArchTaskBarReceiver.h
new file mode 100644
index 0000000..8a925b4
--- /dev/null
+++ b/src/lib/arch/IArchTaskBarReceiver.h
@@ -0,0 +1,98 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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"
+#include "common/IInterface.h"
+
+class IScreen;
+class INode;
+
+//! Interface for architecture dependent task bar event handling
+/*!
+This interface defines the task bar icon event handlers required
+by barrier. Each architecture must implement this interface
+though each operation can be a no-op.
+*/
+class IArchTaskBarReceiver : public IInterface {
+public:
+ // Icon data is architecture dependent
+ typedef void* Icon;
+
+ //! @name manipulators
+ //@{
+
+ //! Show status window
+ /*!
+ Open a window displaying current status. This should return
+ immediately without waiting for the window to be closed.
+ */
+ virtual void showStatus() = 0;
+
+ //! Popup menu
+ /*!
+ Popup a menu of operations at or around \c x,y and perform the
+ chosen operation.
+ */
+ virtual void runMenu(int x, int y) = 0;
+
+ //! Perform primary action
+ /*!
+ Perform the primary (default) action.
+ */
+ virtual void primaryAction() = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Lock receiver
+ /*!
+ Locks the receiver from changing state. The receiver should be
+ locked when querying it's state to ensure consistent results.
+ Each call to \c lock() must have a matching \c unlock() and
+ locks cannot be nested.
+ */
+ virtual void lock() const = 0;
+
+ //! Unlock receiver
+ virtual void unlock() const = 0;
+
+ //! Get icon
+ /*!
+ Returns the icon to display in the task bar. The interface
+ to set the icon is left to subclasses. Getting and setting
+ the icon must be thread safe.
+ */
+ virtual const Icon getIcon() const = 0;
+
+ //! Get tooltip
+ /*!
+ Returns the tool tip to display in the task bar. The interface
+ to set the tooltip is left to sublclasses. Getting and setting
+ the icon must be thread safe.
+ */
+ virtual std::string getToolTip() const = 0;
+
+ virtual void updateStatus(INode*, const String& errorMsg) = 0;
+
+ virtual void cleanup() {}
+
+ //@}
+};
diff --git a/src/lib/arch/IArchTime.h b/src/lib/arch/IArchTime.h
new file mode 100644
index 0000000..abb3cdd
--- /dev/null
+++ b/src/lib/arch/IArchTime.h
@@ -0,0 +1,41 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+
+//! Interface for architecture dependent time operations
+/*!
+This interface defines the time operations required by
+barrier. Each architecture must implement this interface.
+*/
+class IArchTime : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Get the current time
+ /*!
+ Returns the number of seconds since some arbitrary starting time.
+ This should return as high a precision as reasonable.
+ */
+ virtual double time() = 0;
+
+ //@}
+};
diff --git a/src/lib/arch/XArch.h b/src/lib/arch/XArch.h
new file mode 100644
index 0000000..457c620
--- /dev/null
+++ b/src/lib/arch/XArch.h
@@ -0,0 +1,161 @@
+/*
+ * barrier -- 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 "common/common.h"
+#include "common/stdstring.h"
+#include "common/stdexcept.h"
+
+//! Generic thread exception
+/*!
+Exceptions derived from this class are used by the multithreading
+library to perform stack unwinding when a thread terminates. These
+exceptions must always be rethrown by clients when caught.
+*/
+class XThread { };
+
+//! Thread exception to cancel
+/*!
+Thrown to cancel a thread. Clients must not throw this type, but
+must rethrow it if caught (by XThreadCancel, XThread, or ...).
+*/
+class XThreadCancel : public XThread { };
+
+/*!
+\def RETHROW_XTHREAD
+Convenience macro to rethrow an XThread exception but ignore other
+exceptions. Put this in your catch (...) handler after necessary
+cleanup but before leaving or returning from the handler.
+*/
+#define RETHROW_XTHREAD \
+ try { throw; } catch (XThread&) { throw; } catch (...) { }
+
+//! Lazy error message string evaluation
+/*!
+This class encapsulates platform dependent error string lookup.
+Platforms subclass this type, taking an appropriate error code
+type in the c'tor and overriding eval() to return the error
+string for that error code.
+*/
+class XArchEval {
+public:
+ XArchEval() { }
+ virtual ~XArchEval() _NOEXCEPT { }
+
+ virtual std::string eval() const = 0;
+};
+
+//! Generic exception architecture dependent library
+class XArch : public std::runtime_error {
+public:
+ XArch(XArchEval* adopted) : std::runtime_error(adopted->eval()) { delete adopted; }
+ XArch(const std::string& msg) : std::runtime_error(msg) { }
+ virtual ~XArch() _NOEXCEPT { }
+};
+
+// Macro to declare XArch derived types
+#define XARCH_SUBCLASS(name_, super_) \
+class name_ : public super_ { \
+public: \
+ name_(XArchEval* adoptedEvaluator) : super_(adoptedEvaluator) { } \
+ name_(const std::string& msg) : super_(msg) { } \
+}
+
+//! Generic network exception
+/*!
+Exceptions derived from this class are used by the networking
+library to indicate various errors.
+*/
+XARCH_SUBCLASS(XArchNetwork, XArch);
+
+//! Operation was interrupted
+XARCH_SUBCLASS(XArchNetworkInterrupted, XArchNetwork);
+
+//! Network insufficient permission
+XARCH_SUBCLASS(XArchNetworkAccess, XArchNetwork);
+
+//! Network insufficient resources
+XARCH_SUBCLASS(XArchNetworkResource, XArchNetwork);
+
+//! No support for requested network resource/service
+XARCH_SUBCLASS(XArchNetworkSupport, XArchNetwork);
+
+//! Network I/O error
+XARCH_SUBCLASS(XArchNetworkIO, XArchNetwork);
+
+//! Network address is unavailable or not local
+XARCH_SUBCLASS(XArchNetworkNoAddress, XArchNetwork);
+
+//! Network address in use
+XARCH_SUBCLASS(XArchNetworkAddressInUse, XArchNetwork);
+
+//! No route to address
+XARCH_SUBCLASS(XArchNetworkNoRoute, XArchNetwork);
+
+//! Socket not connected
+XARCH_SUBCLASS(XArchNetworkNotConnected, XArchNetwork);
+
+//! Remote read end of socket has closed
+XARCH_SUBCLASS(XArchNetworkShutdown, XArchNetwork);
+
+//! Remote end of socket has disconnected
+XARCH_SUBCLASS(XArchNetworkDisconnected, XArchNetwork);
+
+//! Remote end of socket refused connection
+XARCH_SUBCLASS(XArchNetworkConnectionRefused, XArchNetwork);
+
+//! Remote end of socket is not responding
+XARCH_SUBCLASS(XArchNetworkTimedOut, XArchNetwork);
+
+//! Generic network name lookup erros
+XARCH_SUBCLASS(XArchNetworkName, XArchNetwork);
+
+//! The named host is unknown
+XARCH_SUBCLASS(XArchNetworkNameUnknown, XArchNetworkName);
+
+//! The named host is known but has no address
+XARCH_SUBCLASS(XArchNetworkNameNoAddress, XArchNetworkName);
+
+//! Non-recoverable name server error
+XARCH_SUBCLASS(XArchNetworkNameFailure, XArchNetworkName);
+
+//! Temporary name server error
+XARCH_SUBCLASS(XArchNetworkNameUnavailable, XArchNetworkName);
+
+//! The named host is known but no supported address
+XARCH_SUBCLASS(XArchNetworkNameUnsupported, XArchNetworkName);
+
+//! Generic daemon exception
+/*!
+Exceptions derived from this class are used by the daemon
+library to indicate various errors.
+*/
+XARCH_SUBCLASS(XArchDaemon, XArch);
+
+//! Could not daemonize
+XARCH_SUBCLASS(XArchDaemonFailed, XArchDaemon);
+
+//! Could not install daemon
+XARCH_SUBCLASS(XArchDaemonInstallFailed, XArchDaemon);
+
+//! Could not uninstall daemon
+XARCH_SUBCLASS(XArchDaemonUninstallFailed, XArchDaemon);
+
+//! Attempted to uninstall a daemon that was not installed
+XARCH_SUBCLASS(XArchDaemonUninstallNotInstalled, XArchDaemonUninstallFailed);
diff --git a/src/lib/arch/multibyte.h b/src/lib/arch/multibyte.h
new file mode 100644
index 0000000..4a4e0ec
--- /dev/null
+++ b/src/lib/arch/multibyte.h
@@ -0,0 +1,56 @@
+/*
+ * barrier -- 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 "common/common.h"
+#include "arch/Arch.h"
+
+#include <climits>
+#include <cstring>
+#include <cstdlib>
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+#if HAVE_WCHAR_H || defined(_MSC_VER)
+# include <wchar.h>
+#elif __APPLE__
+ // wtf? Darwin puts mbtowc() et al. in stdlib
+# include <cstdlib>
+#else
+ // platform apparently has no wchar_t support. provide dummy
+ // implementations. hopefully at least the C++ compiler has
+ // a built-in wchar_t type.
+
+static inline
+int
+mbtowc(wchar_t* dst, const char* src, int n)
+{
+ *dst = static_cast<wchar_t>(*src);
+ return 1;
+}
+
+static inline
+int
+wctomb(char* dst, wchar_t src)
+{
+ *dst = static_cast<char>(src);
+ return 1;
+}
+
+#endif
diff --git a/src/lib/arch/unix/ArchConsoleUnix.cpp b/src/lib/arch/unix/ArchConsoleUnix.cpp
new file mode 100644
index 0000000..79a4634
--- /dev/null
+++ b/src/lib/arch/unix/ArchConsoleUnix.cpp
@@ -0,0 +1,23 @@
+/*
+ * barrier -- 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 "arch/unix/ArchConsoleUnix.h"
+
+ArchConsoleUnix::ArchConsoleUnix() { }
+
+ArchConsoleUnix::~ArchConsoleUnix() { }
diff --git a/src/lib/arch/unix/ArchConsoleUnix.h b/src/lib/arch/unix/ArchConsoleUnix.h
new file mode 100644
index 0000000..8326ab5
--- /dev/null
+++ b/src/lib/arch/unix/ArchConsoleUnix.h
@@ -0,0 +1,29 @@
+/*
+ * barrier -- 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 "arch/ArchConsoleStd.h"
+
+#define ARCH_CONSOLE ArchConsoleUnix
+
+class ArchConsoleUnix : public ArchConsoleStd {
+public:
+ ArchConsoleUnix();
+ virtual ~ArchConsoleUnix();
+};
diff --git a/src/lib/arch/unix/ArchDaemonUnix.cpp b/src/lib/arch/unix/ArchDaemonUnix.cpp
new file mode 100644
index 0000000..a03bf7a
--- /dev/null
+++ b/src/lib/arch/unix/ArchDaemonUnix.cpp
@@ -0,0 +1,132 @@
+/*
+ * barrier -- 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 "arch/unix/ArchDaemonUnix.h"
+
+#include "arch/unix/XArchUnix.h"
+#include "base/Log.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <cstdlib>
+
+//
+// ArchDaemonUnix
+//
+
+ArchDaemonUnix::ArchDaemonUnix()
+{
+ // do nothing
+}
+
+ArchDaemonUnix::~ArchDaemonUnix()
+{
+ // do nothing
+}
+
+
+#ifdef __APPLE__
+
+// In Mac OS X, fork()'d child processes can't use most APIs (the frameworks
+// that Barrier uses in fact prevent it and make the process just up and die),
+// so need to exec a copy of the program that doesn't fork so isn't limited.
+int
+execSelfNonDaemonized()
+{
+ extern char** NXArgv;
+ char** selfArgv = NXArgv;
+
+ setenv("_BARRIER_DAEMONIZED", "", 1);
+
+ execvp(selfArgv[0], selfArgv);
+ return 0;
+}
+
+bool alreadyDaemonized() {
+ return getenv("_BARRIER_DAEMONIZED") != NULL;
+}
+
+#endif
+
+int
+ArchDaemonUnix::daemonize(const char* name, DaemonFunc func)
+{
+#ifdef __APPLE__
+ if (alreadyDaemonized())
+ return func(1, &name);
+#endif
+
+ // fork so shell thinks we're done and so we're not a process
+ // group leader
+ switch (fork()) {
+ case -1:
+ // failed
+ throw XArchDaemonFailed(new XArchEvalUnix(errno));
+
+ case 0:
+ // child
+ break;
+
+ default:
+ // parent exits
+ exit(0);
+ }
+
+ // become leader of a new session
+ setsid();
+
+#ifndef __APPLE__
+ // NB: don't run chdir on apple; causes strange behaviour.
+ // chdir to root so we don't keep mounted filesystems points busy
+ // TODO: this is a bit of a hack - can we find a better solution?
+ int chdirErr = chdir("/");
+ if (chdirErr)
+ // NB: file logging actually isn't working at this point!
+ LOG((CLOG_ERR "chdir error: %i", chdirErr));
+#endif
+
+ // mask off permissions for any but owner
+ umask(077);
+
+ // close open files. we only expect stdin, stdout, stderr to be open.
+ close(0);
+ close(1);
+ close(2);
+
+ // attach file descriptors 0, 1, 2 to /dev/null so inadvertent use
+ // of standard I/O safely goes in the bit bucket.
+ open("/dev/null", O_RDONLY);
+ open("/dev/null", O_RDWR);
+
+ int dupErr = dup(1);
+
+ if (dupErr < 0) {
+ // NB: file logging actually isn't working at this point!
+ LOG((CLOG_ERR "dup error: %i", dupErr));
+ }
+
+#ifdef __APPLE__
+ return execSelfNonDaemonized();
+#endif
+
+ // invoke function
+ return func(1, &name);
+}
diff --git a/src/lib/arch/unix/ArchDaemonUnix.h b/src/lib/arch/unix/ArchDaemonUnix.h
new file mode 100644
index 0000000..530159a
--- /dev/null
+++ b/src/lib/arch/unix/ArchDaemonUnix.h
@@ -0,0 +1,36 @@
+/*
+ * barrier -- 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 "arch/ArchDaemonNone.h"
+
+#undef ARCH_DAEMON
+#define ARCH_DAEMON ArchDaemonUnix
+
+//! Unix implementation of IArchDaemon
+class ArchDaemonUnix : public ArchDaemonNone {
+public:
+ ArchDaemonUnix();
+ virtual ~ArchDaemonUnix();
+
+ // IArchDaemon overrides
+ virtual int daemonize(const char* name, DaemonFunc func);
+};
+
+#define CONFIG_FILE "/etc/barrier/barrierd.conf"
diff --git a/src/lib/arch/unix/ArchFileUnix.cpp b/src/lib/arch/unix/ArchFileUnix.cpp
new file mode 100644
index 0000000..d9ae8b2
--- /dev/null
+++ b/src/lib/arch/unix/ArchFileUnix.cpp
@@ -0,0 +1,163 @@
+/*
+ * barrier -- 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 "arch/unix/ArchFileUnix.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <cstring>
+
+//
+// ArchFileUnix
+//
+
+ArchFileUnix::ArchFileUnix()
+{
+ // do nothing
+}
+
+ArchFileUnix::~ArchFileUnix()
+{
+ // do nothing
+}
+
+const char*
+ArchFileUnix::getBasename(const char* pathname)
+{
+ if (pathname == NULL) {
+ return NULL;
+ }
+
+ const char* basename = strrchr(pathname, '/');
+ if (basename != NULL) {
+ return basename + 1;
+ }
+ else {
+ return pathname;
+ }
+}
+
+std::string
+ArchFileUnix::getUserDirectory()
+{
+ char* buffer = NULL;
+ std::string dir;
+#if HAVE_GETPWUID_R
+ struct passwd pwent;
+ struct passwd* pwentp;
+#if defined(_SC_GETPW_R_SIZE_MAX)
+ long size = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (size == -1) {
+ size = BUFSIZ;
+ }
+#else
+ long size = BUFSIZ;
+#endif
+ buffer = new char[size];
+ getpwuid_r(getuid(), &pwent, buffer, size, &pwentp);
+#else
+ struct passwd* pwentp = getpwuid(getuid());
+#endif
+ if (pwentp != NULL && pwentp->pw_dir != NULL) {
+ dir = pwentp->pw_dir;
+ }
+ delete[] buffer;
+ return dir;
+}
+
+std::string
+ArchFileUnix::getSystemDirectory()
+{
+ return "/etc";
+}
+
+std::string
+ArchFileUnix::getInstalledDirectory()
+{
+#if WINAPI_XWINDOWS
+ return "/usr/bin";
+#else
+ return "/Applications/Barrier.app/Contents/MacOS";
+#endif
+}
+
+std::string
+ArchFileUnix::getLogDirectory()
+{
+ return "/var/log";
+}
+
+std::string
+ArchFileUnix::getPluginDirectory()
+{
+ if (!m_pluginDirectory.empty()) {
+ return m_pluginDirectory;
+ }
+
+#if WINAPI_XWINDOWS
+ return getProfileDirectory().append("/plugins");
+#else
+ return getProfileDirectory().append("/Plugins");
+#endif
+}
+
+std::string
+ArchFileUnix::getProfileDirectory()
+{
+ String dir;
+ if (!m_profileDirectory.empty()) {
+ dir = m_profileDirectory;
+ }
+ else {
+#if WINAPI_XWINDOWS
+ dir = getUserDirectory().append("/.barrier");
+#else
+ dir = getUserDirectory().append("/Library/Application Support/Barrier");
+#endif
+ }
+ return dir;
+
+}
+
+std::string
+ArchFileUnix::concatPath(const std::string& prefix,
+ const std::string& suffix)
+{
+ std::string path;
+ path.reserve(prefix.size() + 1 + suffix.size());
+ path += prefix;
+ if (path.size() == 0 || path[path.size() - 1] != '/') {
+ path += '/';
+ }
+ path += suffix;
+ return path;
+}
+
+void
+ArchFileUnix::setProfileDirectory(const String& s)
+{
+ m_profileDirectory = s;
+}
+
+void
+ArchFileUnix::setPluginDirectory(const String& s)
+{
+ m_pluginDirectory = s;
+}
diff --git a/src/lib/arch/unix/ArchFileUnix.h b/src/lib/arch/unix/ArchFileUnix.h
new file mode 100644
index 0000000..86dea0c
--- /dev/null
+++ b/src/lib/arch/unix/ArchFileUnix.h
@@ -0,0 +1,47 @@
+/*
+ * barrier -- 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 "arch/IArchFile.h"
+
+#define ARCH_FILE ArchFileUnix
+
+//! Unix implementation of IArchFile
+class ArchFileUnix : public IArchFile {
+public:
+ ArchFileUnix();
+ virtual ~ArchFileUnix();
+
+ // IArchFile overrides
+ virtual const char* getBasename(const char* pathname);
+ virtual std::string getUserDirectory();
+ virtual std::string getSystemDirectory();
+ virtual std::string getInstalledDirectory();
+ virtual std::string getLogDirectory();
+ virtual std::string getPluginDirectory();
+ virtual std::string getProfileDirectory();
+ virtual std::string concatPath(const std::string& prefix,
+ const std::string& suffix);
+ virtual void setProfileDirectory(const String& s);
+ virtual void setPluginDirectory(const String& s);
+
+private:
+ String m_profileDirectory;
+ String m_pluginDirectory;
+};
diff --git a/src/lib/arch/unix/ArchInternetUnix.cpp b/src/lib/arch/unix/ArchInternetUnix.cpp
new file mode 100644
index 0000000..fd1e135
--- /dev/null
+++ b/src/lib/arch/unix/ArchInternetUnix.cpp
@@ -0,0 +1,126 @@
+/*
+ * barrier -- 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 "arch/unix/ArchInternetUnix.h"
+
+#include "arch/XArch.h"
+#include "common/Version.h"
+#include "base/Log.h"
+
+#include <sstream>
+#include <curl/curl.h>
+
+class CurlFacade {
+public:
+ CurlFacade();
+ ~CurlFacade();
+ String get(const String& url);
+ String urlEncode(const String& url);
+
+private:
+ CURL* m_curl;
+};
+
+//
+// ArchInternetUnix
+//
+
+String
+ArchInternetUnix::get(const String& url)
+{
+ CurlFacade curl;
+ return curl.get(url);
+}
+
+String
+ArchInternetUnix::urlEncode(const String& url)
+{
+ CurlFacade curl;
+ return curl.urlEncode(url);
+}
+
+//
+// CurlFacade
+//
+
+static size_t
+curlWriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
+{
+ ((std::string*)userp)->append((char*)contents, size * nmemb);
+ return size * nmemb;
+}
+
+CurlFacade::CurlFacade() :
+ m_curl(NULL)
+{
+ CURLcode init = curl_global_init(CURL_GLOBAL_ALL);
+ if (init != CURLE_OK) {
+ throw XArch("CURL global init failed.");
+ }
+
+ m_curl = curl_easy_init();
+ if (m_curl == NULL) {
+ throw XArch("CURL easy init failed.");
+ }
+}
+
+CurlFacade::~CurlFacade()
+{
+ if (m_curl != NULL) {
+ curl_easy_cleanup(m_curl);
+ }
+
+ curl_global_cleanup();
+}
+
+String
+CurlFacade::get(const String& url)
+{
+ curl_easy_setopt(m_curl, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, curlWriteCallback);
+
+ std::stringstream userAgent;
+ userAgent << "Barrier ";
+ userAgent << kVersion;
+ curl_easy_setopt(m_curl, CURLOPT_USERAGENT, userAgent.str().c_str());
+
+ std::string result;
+ curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &result);
+
+ CURLcode code = curl_easy_perform(m_curl);
+ if (code != CURLE_OK) {
+ LOG((CLOG_ERR "curl perform error: %s", curl_easy_strerror(code)));
+ throw XArch("CURL perform failed.");
+ }
+
+ return result;
+}
+
+String
+CurlFacade::urlEncode(const String& url)
+{
+ char* resultCStr = curl_easy_escape(m_curl, url.c_str(), 0);
+
+ if (resultCStr == NULL) {
+ throw XArch("CURL escape failed.");
+ }
+
+ std::string result(resultCStr);
+ curl_free(resultCStr);
+
+ return result;
+}
diff --git a/src/lib/arch/unix/ArchInternetUnix.h b/src/lib/arch/unix/ArchInternetUnix.h
new file mode 100644
index 0000000..2413d8f
--- /dev/null
+++ b/src/lib/arch/unix/ArchInternetUnix.h
@@ -0,0 +1,28 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#define ARCH_INTERNET ArchInternetUnix
+
+#include "base/String.h"
+
+class ArchInternetUnix {
+public:
+ String get(const String& url);
+ String urlEncode(const String& url);
+};
diff --git a/src/lib/arch/unix/ArchLogUnix.cpp b/src/lib/arch/unix/ArchLogUnix.cpp
new file mode 100644
index 0000000..b1f9089
--- /dev/null
+++ b/src/lib/arch/unix/ArchLogUnix.cpp
@@ -0,0 +1,84 @@
+/*
+ * barrier -- 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 "arch/unix/ArchLogUnix.h"
+
+#include <syslog.h>
+
+//
+// ArchLogUnix
+//
+
+ArchLogUnix::ArchLogUnix()
+{
+ // do nothing
+}
+
+ArchLogUnix::~ArchLogUnix()
+{
+ // do nothing
+}
+
+void
+ArchLogUnix::openLog(const char* name)
+{
+ openlog(name, 0, LOG_DAEMON);
+}
+
+void
+ArchLogUnix::closeLog()
+{
+ closelog();
+}
+
+void
+ArchLogUnix::showLog(bool)
+{
+ // do nothing
+}
+
+void
+ArchLogUnix::writeLog(ELevel level, const char* msg)
+{
+ // convert level
+ int priority;
+ switch (level) {
+ case kERROR:
+ priority = LOG_ERR;
+ break;
+
+ case kWARNING:
+ priority = LOG_WARNING;
+ break;
+
+ case kNOTE:
+ priority = LOG_NOTICE;
+ break;
+
+ case kINFO:
+ priority = LOG_INFO;
+ break;
+
+ default:
+ priority = LOG_DEBUG;
+ break;
+ }
+
+ // log it
+ syslog(priority, "%s", msg);
+}
diff --git a/src/lib/arch/unix/ArchLogUnix.h b/src/lib/arch/unix/ArchLogUnix.h
new file mode 100644
index 0000000..cdd733f
--- /dev/null
+++ b/src/lib/arch/unix/ArchLogUnix.h
@@ -0,0 +1,36 @@
+/*
+ * barrier -- 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 "arch/IArchLog.h"
+
+#define ARCH_LOG ArchLogUnix
+
+//! Unix implementation of IArchLog
+class ArchLogUnix : public IArchLog {
+public:
+ ArchLogUnix();
+ virtual ~ArchLogUnix();
+
+ // IArchLog overrides
+ virtual void openLog(const char* name);
+ virtual void closeLog();
+ virtual void showLog(bool);
+ virtual void writeLog(ELevel, const char*);
+};
diff --git a/src/lib/arch/unix/ArchMultithreadPosix.cpp b/src/lib/arch/unix/ArchMultithreadPosix.cpp
new file mode 100644
index 0000000..ade6c51
--- /dev/null
+++ b/src/lib/arch/unix/ArchMultithreadPosix.cpp
@@ -0,0 +1,812 @@
+/*
+ * barrier -- 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 "arch/unix/ArchMultithreadPosix.h"
+
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+
+#include <signal.h>
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#include <cerrno>
+
+#define SIGWAKEUP SIGUSR1
+
+#if !HAVE_PTHREAD_SIGNAL
+ // boy, is this platform broken. forget about pthread signal
+ // handling and let signals through to every process. barrier
+ // will not terminate cleanly when it gets SIGTERM or SIGINT.
+# define pthread_sigmask sigprocmask
+# define pthread_kill(tid_, sig_) kill(0, (sig_))
+# define sigwait(set_, sig_)
+# undef HAVE_POSIX_SIGWAIT
+# define HAVE_POSIX_SIGWAIT 1
+#endif
+
+static
+void
+setSignalSet(sigset_t* sigset)
+{
+ sigemptyset(sigset);
+ sigaddset(sigset, SIGHUP);
+ sigaddset(sigset, SIGINT);
+ sigaddset(sigset, SIGTERM);
+ sigaddset(sigset, SIGUSR2);
+}
+
+//
+// ArchThreadImpl
+//
+
+class ArchThreadImpl {
+public:
+ ArchThreadImpl();
+
+public:
+ int m_refCount;
+ IArchMultithread::ThreadID m_id;
+ pthread_t m_thread;
+ IArchMultithread::ThreadFunc m_func;
+ void* m_userData;
+ bool m_cancel;
+ bool m_cancelling;
+ bool m_exited;
+ void* m_result;
+ void* m_networkData;
+};
+
+ArchThreadImpl::ArchThreadImpl() :
+ m_refCount(1),
+ m_id(0),
+ m_func(NULL),
+ m_userData(NULL),
+ m_cancel(false),
+ m_cancelling(false),
+ m_exited(false),
+ m_result(NULL),
+ m_networkData(NULL)
+{
+ // do nothing
+}
+
+
+//
+// ArchMultithreadPosix
+//
+
+ArchMultithreadPosix* ArchMultithreadPosix::s_instance = NULL;
+
+ArchMultithreadPosix::ArchMultithreadPosix() :
+ m_newThreadCalled(false),
+ m_nextID(0)
+{
+ assert(s_instance == NULL);
+
+ s_instance = this;
+
+ // no signal handlers
+ for (size_t i = 0; i < kNUM_SIGNALS; ++i) {
+ m_signalFunc[i] = NULL;
+ m_signalUserData[i] = NULL;
+ }
+
+ // create mutex for thread list
+ m_threadMutex = newMutex();
+
+ // create thread for calling (main) thread and add it to our
+ // list. no need to lock the mutex since we're the only thread.
+ m_mainThread = new ArchThreadImpl;
+ m_mainThread->m_thread = pthread_self();
+ insert(m_mainThread);
+
+ // install SIGWAKEUP handler. this causes SIGWAKEUP to interrupt
+ // system calls. we use that when cancelling a thread to force it
+ // to wake up immediately if it's blocked in a system call. we
+ // won't need this until another thread is created but it's fine
+ // to install it now.
+ struct sigaction act;
+ sigemptyset(&act.sa_mask);
+# if defined(SA_INTERRUPT)
+ act.sa_flags = SA_INTERRUPT;
+# else
+ act.sa_flags = 0;
+# endif
+ act.sa_handler = &threadCancel;
+ sigaction(SIGWAKEUP, &act, NULL);
+
+ // set desired signal dispositions. let SIGWAKEUP through but
+ // ignore SIGPIPE (we'll handle EPIPE).
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGWAKEUP);
+ pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
+ sigemptyset(&sigset);
+ sigaddset(&sigset, SIGPIPE);
+ pthread_sigmask(SIG_BLOCK, &sigset, NULL);
+}
+
+ArchMultithreadPosix::~ArchMultithreadPosix()
+{
+ assert(s_instance != NULL);
+
+ closeMutex(m_threadMutex);
+ s_instance = NULL;
+}
+
+void
+ArchMultithreadPosix::setNetworkDataForCurrentThread(void* data)
+{
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = find(pthread_self());
+ thread->m_networkData = data;
+ unlockMutex(m_threadMutex);
+}
+
+void*
+ArchMultithreadPosix::getNetworkDataForThread(ArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* data = thread->m_networkData;
+ unlockMutex(m_threadMutex);
+ return data;
+}
+
+ArchMultithreadPosix*
+ArchMultithreadPosix::getInstance()
+{
+ return s_instance;
+}
+
+ArchCond
+ArchMultithreadPosix::newCondVar()
+{
+ ArchCondImpl* cond = new ArchCondImpl;
+ int status = pthread_cond_init(&cond->m_cond, NULL);
+ (void)status;
+ assert(status == 0);
+ return cond;
+}
+
+void
+ArchMultithreadPosix::closeCondVar(ArchCond cond)
+{
+ int status = pthread_cond_destroy(&cond->m_cond);
+ (void)status;
+ assert(status == 0);
+ delete cond;
+}
+
+void
+ArchMultithreadPosix::signalCondVar(ArchCond cond)
+{
+ int status = pthread_cond_signal(&cond->m_cond);
+ (void)status;
+ assert(status == 0);
+}
+
+void
+ArchMultithreadPosix::broadcastCondVar(ArchCond cond)
+{
+ int status = pthread_cond_broadcast(&cond->m_cond);
+ (void)status;
+ assert(status == 0);
+}
+
+bool
+ArchMultithreadPosix::waitCondVar(ArchCond cond,
+ ArchMutex mutex, double timeout)
+{
+ // we can't wait on a condition variable and also wake it up for
+ // cancellation since we don't use posix cancellation. so we
+ // must wake up periodically to check for cancellation. we
+ // can't simply go back to waiting after the check since the
+ // condition may have changed and we'll have lost the signal.
+ // so we have to return to the caller. since the caller will
+ // always check for spurious wakeups the only drawback here is
+ // performance: we're waking up a lot more than desired.
+ static const double maxCancellationLatency = 0.1;
+ if (timeout < 0.0 || timeout > maxCancellationLatency) {
+ timeout = maxCancellationLatency;
+ }
+
+ // see if we should cancel this thread
+ testCancelThread();
+
+ // get final time
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ struct timespec finalTime;
+ finalTime.tv_sec = now.tv_sec;
+ finalTime.tv_nsec = now.tv_usec * 1000;
+ long timeout_sec = (long)timeout;
+ long timeout_nsec = (long)(1.0e+9 * (timeout - timeout_sec));
+ finalTime.tv_sec += timeout_sec;
+ finalTime.tv_nsec += timeout_nsec;
+ if (finalTime.tv_nsec >= 1000000000) {
+ finalTime.tv_nsec -= 1000000000;
+ finalTime.tv_sec += 1;
+ }
+
+ // wait
+ int status = pthread_cond_timedwait(&cond->m_cond,
+ &mutex->m_mutex, &finalTime);
+
+ // check for cancel again
+ testCancelThread();
+
+ switch (status) {
+ case 0:
+ // success
+ return true;
+
+ case ETIMEDOUT:
+ return false;
+
+ default:
+ assert(0 && "condition variable wait error");
+ return false;
+ }
+}
+
+ArchMutex
+ArchMultithreadPosix::newMutex()
+{
+ pthread_mutexattr_t attr;
+ int status = pthread_mutexattr_init(&attr);
+ assert(status == 0);
+ ArchMutexImpl* mutex = new ArchMutexImpl;
+ status = pthread_mutex_init(&mutex->m_mutex, &attr);
+ assert(status == 0);
+ return mutex;
+}
+
+void
+ArchMultithreadPosix::closeMutex(ArchMutex mutex)
+{
+ int status = pthread_mutex_destroy(&mutex->m_mutex);
+ (void)status;
+ assert(status == 0);
+ delete mutex;
+}
+
+void
+ArchMultithreadPosix::lockMutex(ArchMutex mutex)
+{
+ int status = pthread_mutex_lock(&mutex->m_mutex);
+
+ switch (status) {
+ case 0:
+ // success
+ return;
+
+ case EDEADLK:
+ assert(0 && "lock already owned");
+ break;
+
+ case EAGAIN:
+ assert(0 && "too many recursive locks");
+ break;
+
+ default:
+ assert(0 && "unexpected error");
+ break;
+ }
+}
+
+void
+ArchMultithreadPosix::unlockMutex(ArchMutex mutex)
+{
+ int status = pthread_mutex_unlock(&mutex->m_mutex);
+
+ switch (status) {
+ case 0:
+ // success
+ return;
+
+ case EPERM:
+ assert(0 && "thread doesn't own a lock");
+ break;
+
+ default:
+ assert(0 && "unexpected error");
+ break;
+ }
+}
+
+ArchThread
+ArchMultithreadPosix::newThread(ThreadFunc func, void* data)
+{
+ assert(func != NULL);
+
+ // initialize signal handler. we do this here instead of the
+ // constructor so we can avoid daemonizing (using fork())
+ // when there are multiple threads. clients can safely
+ // use condition variables and mutexes before creating a
+ // new thread and they can safely use the only thread
+ // they have access to, the main thread, so they really
+ // can't tell the difference.
+ if (!m_newThreadCalled) {
+ m_newThreadCalled = true;
+#if HAVE_PTHREAD_SIGNAL
+ startSignalHandler();
+#endif
+ }
+
+ lockMutex(m_threadMutex);
+
+ // create thread impl for new thread
+ ArchThreadImpl* thread = new ArchThreadImpl;
+ thread->m_func = func;
+ thread->m_userData = data;
+
+ // create the thread. pthread_create() on RedHat 7.2 smp fails
+ // if passed a NULL attr so use a default attr.
+ pthread_attr_t attr;
+ int status = pthread_attr_init(&attr);
+ if (status == 0) {
+ status = pthread_create(&thread->m_thread, &attr,
+ &ArchMultithreadPosix::threadFunc, thread);
+ pthread_attr_destroy(&attr);
+ }
+
+ // check if thread was started
+ if (status != 0) {
+ // failed to start thread so clean up
+ delete thread;
+ thread = NULL;
+ }
+ else {
+ // add thread to list
+ insert(thread);
+
+ // increment ref count to account for the thread itself
+ refThread(thread);
+ }
+
+ // note that the child thread will wait until we release this mutex
+ unlockMutex(m_threadMutex);
+
+ return thread;
+}
+
+ArchThread
+ArchMultithreadPosix::newCurrentThread()
+{
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = find(pthread_self());
+ unlockMutex(m_threadMutex);
+ assert(thread != NULL);
+ return thread;
+}
+
+void
+ArchMultithreadPosix::closeThread(ArchThread thread)
+{
+ assert(thread != NULL);
+
+ // decrement ref count and clean up thread if no more references
+ if (--thread->m_refCount == 0) {
+ // detach from thread (unless it's the main thread)
+ if (thread->m_func != NULL) {
+ pthread_detach(thread->m_thread);
+ }
+
+ // remove thread from list
+ lockMutex(m_threadMutex);
+ assert(findNoRef(thread->m_thread) == thread);
+ erase(thread);
+ unlockMutex(m_threadMutex);
+
+ // done with thread
+ delete thread;
+ }
+}
+
+ArchThread
+ArchMultithreadPosix::copyThread(ArchThread thread)
+{
+ refThread(thread);
+ return thread;
+}
+
+void
+ArchMultithreadPosix::cancelThread(ArchThread thread)
+{
+ assert(thread != NULL);
+
+ // set cancel and wakeup flags if thread can be cancelled
+ bool wakeup = false;
+ lockMutex(m_threadMutex);
+ if (!thread->m_exited && !thread->m_cancelling) {
+ thread->m_cancel = true;
+ wakeup = true;
+ }
+ unlockMutex(m_threadMutex);
+
+ // force thread to exit system calls if wakeup is true
+ if (wakeup) {
+ pthread_kill(thread->m_thread, SIGWAKEUP);
+ }
+}
+
+void
+ArchMultithreadPosix::setPriorityOfThread(ArchThread thread, int /*n*/)
+{
+ assert(thread != NULL);
+
+ // FIXME
+}
+
+void
+ArchMultithreadPosix::testCancelThread()
+{
+ // find current thread
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = findNoRef(pthread_self());
+ unlockMutex(m_threadMutex);
+
+ // test cancel on thread
+ testCancelThreadImpl(thread);
+}
+
+bool
+ArchMultithreadPosix::wait(ArchThread target, double timeout)
+{
+ assert(target != NULL);
+
+ lockMutex(m_threadMutex);
+
+ // find current thread
+ ArchThreadImpl* self = findNoRef(pthread_self());
+
+ // ignore wait if trying to wait on ourself
+ if (target == self) {
+ unlockMutex(m_threadMutex);
+ return false;
+ }
+
+ // ref the target so it can't go away while we're watching it
+ refThread(target);
+
+ unlockMutex(m_threadMutex);
+
+ try {
+ // do first test regardless of timeout
+ testCancelThreadImpl(self);
+ if (isExitedThread(target)) {
+ closeThread(target);
+ return true;
+ }
+
+ // wait and repeat test if there's a timeout
+ if (timeout != 0.0) {
+ const double start = ARCH->time();
+ do {
+ // wait a little
+ ARCH->sleep(0.05);
+
+ // repeat test
+ testCancelThreadImpl(self);
+ if (isExitedThread(target)) {
+ closeThread(target);
+ return true;
+ }
+
+ // repeat wait and test until timed out
+ } while (timeout < 0.0 || (ARCH->time() - start) <= timeout);
+ }
+
+ closeThread(target);
+ return false;
+ }
+ catch (...) {
+ closeThread(target);
+ throw;
+ }
+}
+
+bool
+ArchMultithreadPosix::isSameThread(ArchThread thread1, ArchThread thread2)
+{
+ return (thread1 == thread2);
+}
+
+bool
+ArchMultithreadPosix::isExitedThread(ArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ bool exited = thread->m_exited;
+ unlockMutex(m_threadMutex);
+ return exited;
+}
+
+void*
+ArchMultithreadPosix::getResultOfThread(ArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* result = thread->m_result;
+ unlockMutex(m_threadMutex);
+ return result;
+}
+
+IArchMultithread::ThreadID
+ArchMultithreadPosix::getIDOfThread(ArchThread thread)
+{
+ return thread->m_id;
+}
+
+void
+ArchMultithreadPosix::setSignalHandler(
+ ESignal signal, SignalFunc func, void* userData)
+{
+ lockMutex(m_threadMutex);
+ m_signalFunc[signal] = func;
+ m_signalUserData[signal] = userData;
+ unlockMutex(m_threadMutex);
+}
+
+void
+ArchMultithreadPosix::raiseSignal(ESignal signal)
+{
+ lockMutex(m_threadMutex);
+ if (m_signalFunc[signal] != NULL) {
+ m_signalFunc[signal](signal, m_signalUserData[signal]);
+ pthread_kill(m_mainThread->m_thread, SIGWAKEUP);
+ }
+ else if (signal == kINTERRUPT || signal == kTERMINATE) {
+ ARCH->cancelThread(m_mainThread);
+ }
+ unlockMutex(m_threadMutex);
+}
+
+void
+ArchMultithreadPosix::startSignalHandler()
+{
+ // set signal mask. the main thread blocks these signals and
+ // the signal handler thread will listen for them.
+ sigset_t sigset, oldsigset;
+ setSignalSet(&sigset);
+ pthread_sigmask(SIG_BLOCK, &sigset, &oldsigset);
+
+ // fire up the INT and TERM signal handler thread. we could
+ // instead arrange to catch and handle these signals but
+ // we'd be unable to cancel the main thread since no pthread
+ // calls are allowed in a signal handler.
+ pthread_attr_t attr;
+ int status = pthread_attr_init(&attr);
+ if (status == 0) {
+ status = pthread_create(&m_signalThread, &attr,
+ &ArchMultithreadPosix::threadSignalHandler,
+ NULL);
+ pthread_attr_destroy(&attr);
+ }
+ if (status != 0) {
+ // can't create thread to wait for signal so don't block
+ // the signals.
+ pthread_sigmask(SIG_UNBLOCK, &oldsigset, NULL);
+ }
+}
+
+ArchThreadImpl*
+ArchMultithreadPosix::find(pthread_t thread)
+{
+ ArchThreadImpl* impl = findNoRef(thread);
+ if (impl != NULL) {
+ refThread(impl);
+ }
+ return impl;
+}
+
+ArchThreadImpl*
+ArchMultithreadPosix::findNoRef(pthread_t thread)
+{
+ // linear search
+ for (ThreadList::const_iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if ((*index)->m_thread == thread) {
+ return *index;
+ }
+ }
+ return NULL;
+}
+
+void
+ArchMultithreadPosix::insert(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+
+ // thread shouldn't already be on the list
+ assert(findNoRef(thread->m_thread) == NULL);
+
+ // set thread id. note that we don't worry about m_nextID
+ // wrapping back to 0 and duplicating thread ID's since the
+ // likelihood of barrier running that long is vanishingly
+ // small.
+ thread->m_id = ++m_nextID;
+
+ // append to list
+ m_threadList.push_back(thread);
+}
+
+void
+ArchMultithreadPosix::erase(ArchThreadImpl* thread)
+{
+ for (ThreadList::iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if (*index == thread) {
+ m_threadList.erase(index);
+ break;
+ }
+ }
+}
+
+void
+ArchMultithreadPosix::refThread(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+ assert(findNoRef(thread->m_thread) != NULL);
+ ++thread->m_refCount;
+}
+
+void
+ArchMultithreadPosix::testCancelThreadImpl(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+
+ // update cancel state
+ lockMutex(m_threadMutex);
+ bool cancel = false;
+ if (thread->m_cancel && !thread->m_cancelling) {
+ thread->m_cancelling = true;
+ thread->m_cancel = false;
+ cancel = true;
+ }
+ unlockMutex(m_threadMutex);
+
+ // unwind thread's stack if cancelling
+ if (cancel) {
+ throw XThreadCancel();
+ }
+}
+
+void*
+ArchMultithreadPosix::threadFunc(void* vrep)
+{
+ // get the thread
+ ArchThreadImpl* thread = static_cast<ArchThreadImpl*>(vrep);
+
+ // setup pthreads
+ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
+
+ // run thread
+ s_instance->doThreadFunc(thread);
+
+ // terminate the thread
+ return NULL;
+}
+
+void
+ArchMultithreadPosix::doThreadFunc(ArchThread thread)
+{
+ // default priority is slightly below normal
+ setPriorityOfThread(thread, 1);
+
+ // wait for parent to initialize this object
+ lockMutex(m_threadMutex);
+ unlockMutex(m_threadMutex);
+
+ void* result = NULL;
+ try {
+ // go
+ result = (*thread->m_func)(thread->m_userData);
+ }
+
+ catch (XThreadCancel&) {
+ // client called cancel()
+ }
+ catch (...) {
+ // note -- don't catch (...) to avoid masking bugs
+ lockMutex(m_threadMutex);
+ thread->m_exited = true;
+ unlockMutex(m_threadMutex);
+ closeThread(thread);
+ throw;
+ }
+
+ // thread has exited
+ lockMutex(m_threadMutex);
+ thread->m_result = result;
+ thread->m_exited = true;
+ unlockMutex(m_threadMutex);
+
+ // done with thread
+ closeThread(thread);
+}
+
+void
+ArchMultithreadPosix::threadCancel(int)
+{
+ // do nothing
+}
+
+void*
+ArchMultithreadPosix::threadSignalHandler(void*)
+{
+ // detach
+ pthread_detach(pthread_self());
+
+ // add signal to mask
+ sigset_t sigset;
+ setSignalSet(&sigset);
+
+ // also wait on SIGABRT. on linux (others?) this thread (process)
+ // will persist after all the other threads evaporate due to an
+ // assert unless we wait on SIGABRT. that means our resources (like
+ // the socket we're listening on) are not released and never will be
+ // until the lingering thread is killed. i don't know why sigwait()
+ // should protect the thread from being killed. note that sigwait()
+ // doesn't actually return if we receive SIGABRT and, for some
+ // reason, we don't have to block SIGABRT.
+ sigaddset(&sigset, SIGABRT);
+
+ // we exit the loop via thread cancellation in sigwait()
+ for (;;) {
+ // wait
+#if HAVE_POSIX_SIGWAIT
+ int signal = 0;
+ sigwait(&sigset, &signal);
+#else
+ sigwait(&sigset);
+#endif
+
+ // if we get here then the signal was raised
+ switch (signal) {
+ case SIGINT:
+ ARCH->raiseSignal(kINTERRUPT);
+ break;
+
+ case SIGTERM:
+ ARCH->raiseSignal(kTERMINATE);
+ break;
+
+ case SIGHUP:
+ ARCH->raiseSignal(kHANGUP);
+ break;
+
+ case SIGUSR2:
+ ARCH->raiseSignal(kUSER);
+ break;
+
+ default:
+ // ignore
+ break;
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/lib/arch/unix/ArchMultithreadPosix.h b/src/lib/arch/unix/ArchMultithreadPosix.h
new file mode 100644
index 0000000..98b5eda
--- /dev/null
+++ b/src/lib/arch/unix/ArchMultithreadPosix.h
@@ -0,0 +1,115 @@
+/*
+ * barrier -- 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 "arch/IArchMultithread.h"
+#include "common/stdlist.h"
+
+#include <pthread.h>
+
+#define ARCH_MULTITHREAD ArchMultithreadPosix
+
+class ArchCondImpl {
+public:
+ pthread_cond_t m_cond;
+};
+
+class ArchMutexImpl {
+public:
+ pthread_mutex_t m_mutex;
+};
+
+//! Posix implementation of IArchMultithread
+class ArchMultithreadPosix : public IArchMultithread {
+public:
+ ArchMultithreadPosix();
+ virtual ~ArchMultithreadPosix();
+
+ //! @name manipulators
+ //@{
+
+ void setNetworkDataForCurrentThread(void*);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ void* getNetworkDataForThread(ArchThread);
+
+ static ArchMultithreadPosix* getInstance();
+
+ //@}
+
+ // IArchMultithread overrides
+ virtual ArchCond newCondVar();
+ virtual void closeCondVar(ArchCond);
+ virtual void signalCondVar(ArchCond);
+ virtual void broadcastCondVar(ArchCond);
+ virtual bool waitCondVar(ArchCond, ArchMutex, double timeout);
+ virtual ArchMutex newMutex();
+ virtual void closeMutex(ArchMutex);
+ virtual void lockMutex(ArchMutex);
+ virtual void unlockMutex(ArchMutex);
+ virtual ArchThread newThread(ThreadFunc, void*);
+ virtual ArchThread newCurrentThread();
+ virtual ArchThread copyThread(ArchThread);
+ virtual void closeThread(ArchThread);
+ virtual void cancelThread(ArchThread);
+ virtual void setPriorityOfThread(ArchThread, int n);
+ virtual void testCancelThread();
+ virtual bool wait(ArchThread, double timeout);
+ virtual bool isSameThread(ArchThread, ArchThread);
+ virtual bool isExitedThread(ArchThread);
+ virtual void* getResultOfThread(ArchThread);
+ virtual ThreadID getIDOfThread(ArchThread);
+ virtual void setSignalHandler(ESignal, SignalFunc, void*);
+ virtual void raiseSignal(ESignal);
+
+private:
+ void startSignalHandler();
+
+ ArchThreadImpl* find(pthread_t thread);
+ ArchThreadImpl* findNoRef(pthread_t thread);
+ void insert(ArchThreadImpl* thread);
+ void erase(ArchThreadImpl* thread);
+
+ void refThread(ArchThreadImpl* rep);
+ void testCancelThreadImpl(ArchThreadImpl* rep);
+
+ void doThreadFunc(ArchThread thread);
+ static void* threadFunc(void* vrep);
+ static void threadCancel(int);
+ static void* threadSignalHandler(void* vrep);
+
+private:
+ typedef std::list<ArchThread> ThreadList;
+
+ static ArchMultithreadPosix* s_instance;
+
+ bool m_newThreadCalled;
+
+ ArchMutex m_threadMutex;
+ ArchThread m_mainThread;
+ ThreadList m_threadList;
+ ThreadID m_nextID;
+
+ pthread_t m_signalThread;
+ SignalFunc m_signalFunc[kNUM_SIGNALS];
+ void* m_signalUserData[kNUM_SIGNALS];
+};
diff --git a/src/lib/arch/unix/ArchNetworkBSD.cpp b/src/lib/arch/unix/ArchNetworkBSD.cpp
new file mode 100644
index 0000000..149218c
--- /dev/null
+++ b/src/lib/arch/unix/ArchNetworkBSD.cpp
@@ -0,0 +1,1005 @@
+/*
+ * barrier -- 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 "arch/unix/ArchNetworkBSD.h"
+
+#include "arch/unix/ArchMultithreadPosix.h"
+#include "arch/unix/XArchUnix.h"
+#include "arch/Arch.h"
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <netinet/in.h>
+#include <netdb.h>
+#if !defined(TCP_NODELAY)
+# include <netinet/tcp.h>
+#endif
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#if HAVE_POLL
+# include <poll.h>
+#else
+# if HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# endif
+#endif
+
+#if !HAVE_INET_ATON
+# include <stdio.h>
+#endif
+
+static const int s_family[] = {
+ PF_UNSPEC,
+ PF_INET,
+ PF_INET6,
+};
+static const int s_type[] = {
+ SOCK_DGRAM,
+ SOCK_STREAM
+};
+
+#if !HAVE_INET_ATON
+// parse dotted quad addresses. we don't bother with the weird BSD'ism
+// of handling octal and hex and partial forms.
+static
+in_addr_t
+inet_aton(const char* cp, struct in_addr* inp)
+{
+ unsigned int a, b, c, d;
+ if (sscanf(cp, "%u.%u.%u.%u", &a, &b, &c, &d) != 4) {
+ return 0;
+ }
+ if (a >= 256 || b >= 256 || c >= 256 || d >= 256) {
+ return 0;
+ }
+ unsigned char* incp = (unsigned char*)inp;
+ incp[0] = (unsigned char)(a & 0xffu);
+ incp[1] = (unsigned char)(b & 0xffu);
+ incp[2] = (unsigned char)(c & 0xffu);
+ incp[3] = (unsigned char)(d & 0xffu);
+ return inp->s_addr;
+}
+#endif
+
+//
+// ArchNetworkBSD
+//
+
+ArchNetworkBSD::ArchNetworkBSD()
+{
+}
+
+ArchNetworkBSD::~ArchNetworkBSD()
+{
+ ARCH->closeMutex(m_mutex);
+}
+
+void
+ArchNetworkBSD::init()
+{
+ // create mutex to make some calls thread safe
+ m_mutex = ARCH->newMutex();
+}
+
+ArchSocket
+ArchNetworkBSD::newSocket(EAddressFamily family, ESocketType type)
+{
+ // create socket
+ int fd = socket(s_family[family], s_type[type], 0);
+ if (fd == -1) {
+ throwError(errno);
+ }
+ try {
+ setBlockingOnSocket(fd, false);
+ }
+ catch (...) {
+ close(fd);
+ throw;
+ }
+
+ // allocate socket object
+ ArchSocketImpl* newSocket = new ArchSocketImpl;
+ newSocket->m_fd = fd;
+ newSocket->m_refCount = 1;
+ return newSocket;
+}
+
+ArchSocket
+ArchNetworkBSD::copySocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // ref the socket and return it
+ ARCH->lockMutex(m_mutex);
+ ++s->m_refCount;
+ ARCH->unlockMutex(m_mutex);
+ return s;
+}
+
+void
+ArchNetworkBSD::closeSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // unref the socket and note if it should be released
+ ARCH->lockMutex(m_mutex);
+ const bool doClose = (--s->m_refCount == 0);
+ ARCH->unlockMutex(m_mutex);
+
+ // close the socket if necessary
+ if (doClose) {
+ if (close(s->m_fd) == -1) {
+ // close failed. restore the last ref and throw.
+ int err = errno;
+ ARCH->lockMutex(m_mutex);
+ ++s->m_refCount;
+ ARCH->unlockMutex(m_mutex);
+ throwError(err);
+ }
+ delete s;
+ }
+}
+
+void
+ArchNetworkBSD::closeSocketForRead(ArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown(s->m_fd, 0) == -1) {
+ if (errno != ENOTCONN) {
+ throwError(errno);
+ }
+ }
+}
+
+void
+ArchNetworkBSD::closeSocketForWrite(ArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown(s->m_fd, 1) == -1) {
+ if (errno != ENOTCONN) {
+ throwError(errno);
+ }
+ }
+}
+
+void
+ArchNetworkBSD::bindSocket(ArchSocket s, ArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (bind(s->m_fd, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == -1) {
+ throwError(errno);
+ }
+}
+
+void
+ArchNetworkBSD::listenOnSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // hardcoding backlog
+ if (listen(s->m_fd, 3) == -1) {
+ throwError(errno);
+ }
+}
+
+ArchSocket
+ArchNetworkBSD::acceptSocket(ArchSocket s, ArchNetAddress* addr)
+{
+ assert(s != NULL);
+
+ // if user passed NULL in addr then use scratch space
+ ArchNetAddress dummy;
+ if (addr == NULL) {
+ addr = &dummy;
+ }
+
+ // create new socket and address
+ ArchSocketImpl* newSocket = new ArchSocketImpl;
+ *addr = new ArchNetAddressImpl;
+
+ // accept on socket
+ ACCEPT_TYPE_ARG3 len = (ACCEPT_TYPE_ARG3)((*addr)->m_len);
+ int fd = accept(s->m_fd, TYPED_ADDR(struct sockaddr, (*addr)), &len);
+ (*addr)->m_len = (socklen_t)len;
+ if (fd == -1) {
+ int err = errno;
+ delete newSocket;
+ delete *addr;
+ *addr = NULL;
+ if (err == EAGAIN) {
+ return NULL;
+ }
+ throwError(err);
+ }
+
+ try {
+ setBlockingOnSocket(fd, false);
+ }
+ catch (...) {
+ close(fd);
+ delete newSocket;
+ delete *addr;
+ *addr = NULL;
+ throw;
+ }
+
+ // initialize socket
+ newSocket->m_fd = fd;
+ newSocket->m_refCount = 1;
+
+ // discard address if not requested
+ if (addr == &dummy) {
+ ARCH->closeAddr(dummy);
+ }
+
+ return newSocket;
+}
+
+bool
+ArchNetworkBSD::connectSocket(ArchSocket s, ArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (connect(s->m_fd, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == -1) {
+ if (errno == EISCONN) {
+ return true;
+ }
+ if (errno == EINPROGRESS) {
+ return false;
+ }
+ throwError(errno);
+ }
+ return true;
+}
+
+#if HAVE_POLL
+
+int
+ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout)
+{
+ assert(pe != NULL || num == 0);
+
+ // return if nothing to do
+ if (num == 0) {
+ if (timeout > 0.0) {
+ ARCH->sleep(timeout);
+ }
+ return 0;
+ }
+
+ // allocate space for translated query
+ struct pollfd* pfd = new struct pollfd[1 + num];
+
+ // translate query
+ for (int i = 0; i < num; ++i) {
+ pfd[i].fd = (pe[i].m_socket == NULL) ? -1 : pe[i].m_socket->m_fd;
+ pfd[i].events = 0;
+ if ((pe[i].m_events & kPOLLIN) != 0) {
+ pfd[i].events |= POLLIN;
+ }
+ if ((pe[i].m_events & kPOLLOUT) != 0) {
+ pfd[i].events |= POLLOUT;
+ }
+ }
+ int n = num;
+
+ // add the unblock pipe
+ const int* unblockPipe = getUnblockPipe();
+ if (unblockPipe != NULL) {
+ pfd[n].fd = unblockPipe[0];
+ pfd[n].events = POLLIN;
+ ++n;
+ }
+
+ // prepare timeout
+ int t = (timeout < 0.0) ? -1 : static_cast<int>(1000.0 * timeout);
+
+ // do the poll
+ n = poll(pfd, n, t);
+
+ // reset the unblock pipe
+ if (n > 0 && unblockPipe != NULL && (pfd[num].revents & POLLIN) != 0) {
+ // the unblock event was signalled. flush the pipe.
+ char dummy[100];
+ int ignore;
+
+ do {
+ ignore = read(unblockPipe[0], dummy, sizeof(dummy));
+ } while (errno != EAGAIN);
+
+ // don't count this unblock pipe in return value
+ --n;
+ }
+
+ // handle results
+ if (n == -1) {
+ if (errno == EINTR) {
+ // interrupted system call
+ ARCH->testCancelThread();
+ delete[] pfd;
+ return 0;
+ }
+ delete[] pfd;
+ throwError(errno);
+ }
+
+ // translate back
+ for (int i = 0; i < num; ++i) {
+ pe[i].m_revents = 0;
+ if ((pfd[i].revents & POLLIN) != 0) {
+ pe[i].m_revents |= kPOLLIN;
+ }
+ if ((pfd[i].revents & POLLOUT) != 0) {
+ pe[i].m_revents |= kPOLLOUT;
+ }
+ if ((pfd[i].revents & POLLERR) != 0) {
+ pe[i].m_revents |= kPOLLERR;
+ }
+ if ((pfd[i].revents & POLLNVAL) != 0) {
+ pe[i].m_revents |= kPOLLNVAL;
+ }
+ }
+
+ delete[] pfd;
+ return n;
+}
+
+#else
+
+int
+ArchNetworkBSD::pollSocket(PollEntry pe[], int num, double timeout)
+{
+ int i, n;
+
+ // prepare sets for select
+ n = 0;
+ fd_set readSet, writeSet, errSet;
+ fd_set* readSetP = NULL;
+ fd_set* writeSetP = NULL;
+ fd_set* errSetP = NULL;
+ FD_ZERO(&readSet);
+ FD_ZERO(&writeSet);
+ FD_ZERO(&errSet);
+ for (i = 0; i < num; ++i) {
+ // reset return flags
+ pe[i].m_revents = 0;
+
+ // set invalid flag if socket is bogus then go to next socket
+ if (pe[i].m_socket == NULL) {
+ pe[i].m_revents |= kPOLLNVAL;
+ continue;
+ }
+
+ int fdi = pe[i].m_socket->m_fd;
+ if (pe[i].m_events & kPOLLIN) {
+ FD_SET(pe[i].m_socket->m_fd, &readSet);
+ readSetP = &readSet;
+ if (fdi > n) {
+ n = fdi;
+ }
+ }
+ if (pe[i].m_events & kPOLLOUT) {
+ FD_SET(pe[i].m_socket->m_fd, &writeSet);
+ writeSetP = &writeSet;
+ if (fdi > n) {
+ n = fdi;
+ }
+ }
+ if (true) {
+ FD_SET(pe[i].m_socket->m_fd, &errSet);
+ errSetP = &errSet;
+ if (fdi > n) {
+ n = fdi;
+ }
+ }
+ }
+
+ // add the unblock pipe
+ const int* unblockPipe = getUnblockPipe();
+ if (unblockPipe != NULL) {
+ FD_SET(unblockPipe[0], &readSet);
+ readSetP = &readSet;
+ if (unblockPipe[0] > n) {
+ n = unblockPipe[0];
+ }
+ }
+
+ // if there are no sockets then don't block forever
+ if (n == 0 && timeout < 0.0) {
+ timeout = 0.0;
+ }
+
+ // prepare timeout for select
+ struct timeval timeout2;
+ struct timeval* timeout2P;
+ if (timeout < 0.0) {
+ timeout2P = NULL;
+ }
+ else {
+ timeout2P = &timeout2;
+ timeout2.tv_sec = static_cast<int>(timeout);
+ timeout2.tv_usec = static_cast<int>(1.0e+6 *
+ (timeout - timeout2.tv_sec));
+ }
+
+ // do the select
+ n = select((SELECT_TYPE_ARG1) n + 1,
+ SELECT_TYPE_ARG234 readSetP,
+ SELECT_TYPE_ARG234 writeSetP,
+ SELECT_TYPE_ARG234 errSetP,
+ SELECT_TYPE_ARG5 timeout2P);
+
+ // reset the unblock pipe
+ if (n > 0 && unblockPipe != NULL && FD_ISSET(unblockPipe[0], &readSet)) {
+ // the unblock event was signalled. flush the pipe.
+ char dummy[100];
+ do {
+ read(unblockPipe[0], dummy, sizeof(dummy));
+ } while (errno != EAGAIN);
+ }
+
+ // handle results
+ if (n == -1) {
+ if (errno == EINTR) {
+ // interrupted system call
+ ARCH->testCancelThread();
+ return 0;
+ }
+ throwError(errno);
+ }
+ n = 0;
+ for (i = 0; i < num; ++i) {
+ if (pe[i].m_socket != NULL) {
+ if (FD_ISSET(pe[i].m_socket->m_fd, &readSet)) {
+ pe[i].m_revents |= kPOLLIN;
+ }
+ if (FD_ISSET(pe[i].m_socket->m_fd, &writeSet)) {
+ pe[i].m_revents |= kPOLLOUT;
+ }
+ if (FD_ISSET(pe[i].m_socket->m_fd, &errSet)) {
+ pe[i].m_revents |= kPOLLERR;
+ }
+ }
+ if (pe[i].m_revents != 0) {
+ ++n;
+ }
+ }
+
+ return n;
+}
+
+#endif
+
+void
+ArchNetworkBSD::unblockPollSocket(ArchThread thread)
+{
+ const int* unblockPipe = getUnblockPipeForThread(thread);
+ if (unblockPipe != NULL) {
+ char dummy = 0;
+ int ignore;
+
+ ignore = write(unblockPipe[1], &dummy, 1);
+ }
+}
+
+size_t
+ArchNetworkBSD::readSocket(ArchSocket s, void* buf, size_t len)
+{
+ assert(s != NULL);
+
+ ssize_t n = read(s->m_fd, buf, len);
+ if (n == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ return 0;
+ }
+ throwError(errno);
+ }
+ return n;
+}
+
+size_t
+ArchNetworkBSD::writeSocket(ArchSocket s, const void* buf, size_t len)
+{
+ assert(s != NULL);
+
+ ssize_t n = write(s->m_fd, buf, len);
+ if (n == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ return 0;
+ }
+ throwError(errno);
+ }
+ return n;
+}
+
+void
+ArchNetworkBSD::throwErrorOnSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // get the error from the socket layer
+ int err = 0;
+ socklen_t size = (socklen_t)sizeof(err);
+ if (getsockopt(s->m_fd, SOL_SOCKET, SO_ERROR,
+ (optval_t*)&err, &size) == -1) {
+ err = errno;
+ }
+
+ // throw if there's an error
+ if (err != 0) {
+ throwError(err);
+ }
+}
+
+void
+ArchNetworkBSD::setBlockingOnSocket(int fd, bool blocking)
+{
+ assert(fd != -1);
+
+ int mode = fcntl(fd, F_GETFL, 0);
+ if (mode == -1) {
+ throwError(errno);
+ }
+ if (blocking) {
+ mode &= ~O_NONBLOCK;
+ }
+ else {
+ mode |= O_NONBLOCK;
+ }
+ if (fcntl(fd, F_SETFL, mode) == -1) {
+ throwError(errno);
+ }
+}
+
+bool
+ArchNetworkBSD::setNoDelayOnSocket(ArchSocket s, bool noDelay)
+{
+ assert(s != NULL);
+
+ // get old state
+ int oflag;
+ socklen_t size = (socklen_t)sizeof(oflag);
+ if (getsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY,
+ (optval_t*)&oflag, &size) == -1) {
+ throwError(errno);
+ }
+
+ int flag = noDelay ? 1 : 0;
+ size = (socklen_t)sizeof(flag);
+ if (setsockopt(s->m_fd, IPPROTO_TCP, TCP_NODELAY,
+ (optval_t*)&flag, size) == -1) {
+ throwError(errno);
+ }
+
+ return (oflag != 0);
+}
+
+bool
+ArchNetworkBSD::setReuseAddrOnSocket(ArchSocket s, bool reuse)
+{
+ assert(s != NULL);
+
+ // get old state
+ int oflag;
+ socklen_t size = (socklen_t)sizeof(oflag);
+ if (getsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR,
+ (optval_t*)&oflag, &size) == -1) {
+ throwError(errno);
+ }
+
+ int flag = reuse ? 1 : 0;
+ size = (socklen_t)sizeof(flag);
+ if (setsockopt(s->m_fd, SOL_SOCKET, SO_REUSEADDR,
+ (optval_t*)&flag, size) == -1) {
+ throwError(errno);
+ }
+
+ return (oflag != 0);
+}
+
+std::string
+ArchNetworkBSD::getHostName()
+{
+ char name[256];
+ if (gethostname(name, sizeof(name)) == -1) {
+ name[0] = '\0';
+ }
+ else {
+ name[sizeof(name) - 1] = '\0';
+ }
+ return name;
+}
+
+ArchNetAddress
+ArchNetworkBSD::newAnyAddr(EAddressFamily family)
+{
+ // allocate address
+ ArchNetAddressImpl* addr = new ArchNetAddressImpl;
+
+ // fill it in
+ switch (family) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ipAddr->sin_family = AF_INET;
+ ipAddr->sin_port = 0;
+ ipAddr->sin_addr.s_addr = INADDR_ANY;
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
+ break;
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ ipAddr->sin6_family = AF_INET6;
+ ipAddr->sin6_port = 0;
+ memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any));
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in6);
+ break;
+ }
+ default:
+ delete addr;
+ assert(0 && "invalid family");
+ }
+
+ return addr;
+}
+
+ArchNetAddress
+ArchNetworkBSD::copyAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ // allocate and copy address
+ return new ArchNetAddressImpl(*addr);
+}
+
+ArchNetAddress
+ArchNetworkBSD::nameToAddr(const std::string& name)
+{
+ // allocate address
+ ArchNetAddressImpl* addr = new ArchNetAddressImpl;
+
+ char ipstr[INET6_ADDRSTRLEN];
+ struct addrinfo hints;
+ struct addrinfo *p;
+ int ret;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+
+ ARCH->lockMutex(m_mutex);
+ if ((ret = getaddrinfo(name.c_str(), NULL, &hints, &p)) != 0) {
+ ARCH->unlockMutex(m_mutex);
+ delete addr;
+ throwNameError(ret);
+ }
+
+ if (p->ai_family == AF_INET) {
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
+ } else {
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in6);
+ }
+
+ memcpy(&addr->m_addr, p->ai_addr, addr->m_len);
+ freeaddrinfo(p);
+ ARCH->unlockMutex(m_mutex);
+
+ return addr;
+}
+
+void
+ArchNetworkBSD::closeAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ delete addr;
+}
+
+std::string
+ArchNetworkBSD::addrToName(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ // mutexed name lookup (ugh)
+ ARCH->lockMutex(m_mutex);
+ char host[1024];
+ char service[20];
+ int ret = getnameinfo(TYPED_ADDR(struct sockaddr, addr), addr->m_len, host,
+ sizeof(host), service, sizeof(service), 0);
+ if (ret != 0) {
+ ARCH->unlockMutex(m_mutex);
+ throwNameError(ret);
+ }
+
+ // save (primary) name
+ std::string name = host;
+
+ // done with static buffer
+ ARCH->unlockMutex(m_mutex);
+
+ return name;
+}
+
+std::string
+ArchNetworkBSD::addrToString(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ARCH->lockMutex(m_mutex);
+ std::string s = inet_ntoa(ipAddr->sin_addr);
+ ARCH->unlockMutex(m_mutex);
+ return s;
+ }
+
+ case kINET6: {
+ char strAddr[INET6_ADDRSTRLEN];
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ ARCH->lockMutex(m_mutex);
+ inet_ntop(AF_INET6, &ipAddr->sin6_addr, strAddr, INET6_ADDRSTRLEN);
+ ARCH->unlockMutex(m_mutex);
+ return strAddr;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return "";
+ }
+}
+
+IArchNetwork::EAddressFamily
+ArchNetworkBSD::getAddrFamily(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (addr->m_addr.ss_family) {
+ case AF_INET:
+ return kINET;
+ case AF_INET6:
+ return kINET6;
+
+ default:
+ return kUNKNOWN;
+ }
+}
+
+void
+ArchNetworkBSD::setAddrPort(ArchNetAddress addr, int port)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ipAddr->sin_port = htons(port);
+ break;
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ ipAddr->sin6_port = htons(port);
+ break;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ break;
+ }
+}
+
+int
+ArchNetworkBSD::getAddrPort(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ return ntohs(ipAddr->sin_port);
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ return ntohs(ipAddr->sin6_port);
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return 0;
+ }
+}
+
+bool
+ArchNetworkBSD::isAnyAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ return (ipAddr->sin_addr.s_addr == INADDR_ANY &&
+ addr->m_len == (socklen_t)sizeof(struct sockaddr_in));
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ return (memcmp(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0 &&
+ addr->m_len == (socklen_t)sizeof(struct sockaddr_in6));
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return true;
+ }
+}
+
+bool
+ArchNetworkBSD::isEqualAddr(ArchNetAddress a, ArchNetAddress b)
+{
+ return (a->m_len == b->m_len &&
+ memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0);
+}
+
+const int*
+ArchNetworkBSD::getUnblockPipe()
+{
+ ArchMultithreadPosix* mt = ArchMultithreadPosix::getInstance();
+ ArchThread thread = mt->newCurrentThread();
+ const int* p = getUnblockPipeForThread(thread);
+ ARCH->closeThread(thread);
+ return p;
+}
+
+const int*
+ArchNetworkBSD::getUnblockPipeForThread(ArchThread thread)
+{
+ ArchMultithreadPosix* mt = ArchMultithreadPosix::getInstance();
+ int* unblockPipe = (int*)mt->getNetworkDataForThread(thread);
+ if (unblockPipe == NULL) {
+ unblockPipe = new int[2];
+ if (pipe(unblockPipe) != -1) {
+ try {
+ setBlockingOnSocket(unblockPipe[0], false);
+ mt->setNetworkDataForCurrentThread(unblockPipe);
+ }
+ catch (...) {
+ delete[] unblockPipe;
+ unblockPipe = NULL;
+ }
+ }
+ else {
+ delete[] unblockPipe;
+ unblockPipe = NULL;
+ }
+ }
+ return unblockPipe;
+}
+
+void
+ArchNetworkBSD::throwError(int err)
+{
+ switch (err) {
+ case EINTR:
+ ARCH->testCancelThread();
+ throw XArchNetworkInterrupted(new XArchEvalUnix(err));
+
+ case EACCES:
+ case EPERM:
+ throw XArchNetworkAccess(new XArchEvalUnix(err));
+
+ case ENFILE:
+ case EMFILE:
+ case ENODEV:
+ case ENOBUFS:
+ case ENOMEM:
+ case ENETDOWN:
+#if defined(ENOSR)
+ case ENOSR:
+#endif
+ throw XArchNetworkResource(new XArchEvalUnix(err));
+
+ case EPROTOTYPE:
+ case EPROTONOSUPPORT:
+ case EAFNOSUPPORT:
+ case EPFNOSUPPORT:
+ case ESOCKTNOSUPPORT:
+ case EINVAL:
+ case ENOPROTOOPT:
+ case EOPNOTSUPP:
+ case ESHUTDOWN:
+#if defined(ENOPKG)
+ case ENOPKG:
+#endif
+ throw XArchNetworkSupport(new XArchEvalUnix(err));
+
+ case EIO:
+ throw XArchNetworkIO(new XArchEvalUnix(err));
+
+ case EADDRNOTAVAIL:
+ throw XArchNetworkNoAddress(new XArchEvalUnix(err));
+
+ case EADDRINUSE:
+ throw XArchNetworkAddressInUse(new XArchEvalUnix(err));
+
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ throw XArchNetworkNoRoute(new XArchEvalUnix(err));
+
+ case ENOTCONN:
+ throw XArchNetworkNotConnected(new XArchEvalUnix(err));
+
+ case EPIPE:
+ throw XArchNetworkShutdown(new XArchEvalUnix(err));
+
+ case ECONNABORTED:
+ case ECONNRESET:
+ throw XArchNetworkDisconnected(new XArchEvalUnix(err));
+
+ case ECONNREFUSED:
+ throw XArchNetworkConnectionRefused(new XArchEvalUnix(err));
+
+ case EHOSTDOWN:
+ case ETIMEDOUT:
+ throw XArchNetworkTimedOut(new XArchEvalUnix(err));
+
+ default:
+ throw XArchNetwork(new XArchEvalUnix(err));
+ }
+}
+
+void
+ArchNetworkBSD::throwNameError(int err)
+{
+ static const char* s_msg[] = {
+ "The specified host is unknown",
+ "The requested name is valid but does not have an IP address",
+ "A non-recoverable name server error occurred",
+ "A temporary error occurred on an authoritative name server",
+ "An unknown name server error occurred"
+ };
+
+ switch (err) {
+ case HOST_NOT_FOUND:
+ throw XArchNetworkNameUnknown(s_msg[0]);
+
+ case NO_DATA:
+ throw XArchNetworkNameNoAddress(s_msg[1]);
+
+ case NO_RECOVERY:
+ throw XArchNetworkNameFailure(s_msg[2]);
+
+ case TRY_AGAIN:
+ throw XArchNetworkNameUnavailable(s_msg[3]);
+
+ default:
+ throw XArchNetworkName(s_msg[4]);
+ }
+}
diff --git a/src/lib/arch/unix/ArchNetworkBSD.h b/src/lib/arch/unix/ArchNetworkBSD.h
new file mode 100644
index 0000000..3f5679a
--- /dev/null
+++ b/src/lib/arch/unix/ArchNetworkBSD.h
@@ -0,0 +1,105 @@
+/*
+ * barrier -- 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 "arch/IArchNetwork.h"
+#include "arch/IArchMultithread.h"
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+# include <sys/socket.h>
+#endif
+
+#if !HAVE_SOCKLEN_T
+typedef int socklen_t;
+#endif
+
+// old systems may use char* for [gs]etsockopt()'s optval argument.
+// this should be void on modern systems but char is forwards
+// compatible so we always use it.
+typedef char optval_t;
+
+#define ARCH_NETWORK ArchNetworkBSD
+#define TYPED_ADDR(type_, addr_) (reinterpret_cast<type_*>(&addr_->m_addr))
+
+class ArchSocketImpl {
+public:
+ int m_fd;
+ int m_refCount;
+};
+
+class ArchNetAddressImpl {
+public:
+ ArchNetAddressImpl() : m_len(sizeof(m_addr)) { }
+
+public:
+ struct sockaddr_storage m_addr;
+ socklen_t m_len;
+};
+
+//! Berkeley (BSD) sockets implementation of IArchNetwork
+class ArchNetworkBSD : public IArchNetwork {
+public:
+ ArchNetworkBSD();
+ virtual ~ArchNetworkBSD();
+
+ virtual void init();
+
+ // IArchNetwork overrides
+ virtual ArchSocket newSocket(EAddressFamily, ESocketType);
+ virtual ArchSocket copySocket(ArchSocket s); virtual void closeSocket(ArchSocket s);
+ virtual void closeSocketForRead(ArchSocket s);
+ virtual void closeSocketForWrite(ArchSocket s);
+ virtual void bindSocket(ArchSocket s, ArchNetAddress addr);
+ virtual void listenOnSocket(ArchSocket s);
+ virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress* addr);
+ virtual bool connectSocket(ArchSocket s, ArchNetAddress name);
+ virtual int pollSocket(PollEntry[], int num, double timeout);
+ virtual void unblockPollSocket(ArchThread thread);
+ virtual size_t readSocket(ArchSocket s, void* buf, size_t len);
+ virtual size_t writeSocket(ArchSocket s,
+ const void* buf, size_t len);
+ virtual void throwErrorOnSocket(ArchSocket);
+ virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay);
+ virtual bool setReuseAddrOnSocket(ArchSocket, bool reuse);
+ virtual std::string getHostName();
+ virtual ArchNetAddress newAnyAddr(EAddressFamily);
+ virtual ArchNetAddress copyAddr(ArchNetAddress);
+ virtual ArchNetAddress nameToAddr(const std::string&);
+ virtual void closeAddr(ArchNetAddress);
+ virtual std::string addrToName(ArchNetAddress);
+ virtual std::string addrToString(ArchNetAddress);
+ virtual EAddressFamily getAddrFamily(ArchNetAddress);
+ virtual void setAddrPort(ArchNetAddress, int port);
+ virtual int getAddrPort(ArchNetAddress);
+ virtual bool isAnyAddr(ArchNetAddress);
+ virtual bool isEqualAddr(ArchNetAddress, ArchNetAddress);
+
+private:
+ const int* getUnblockPipe();
+ const int* getUnblockPipeForThread(ArchThread);
+ void setBlockingOnSocket(int fd, bool blocking);
+ void throwError(int);
+ void throwNameError(int);
+
+private:
+ ArchMutex m_mutex;
+};
diff --git a/src/lib/arch/unix/ArchSleepUnix.cpp b/src/lib/arch/unix/ArchSleepUnix.cpp
new file mode 100644
index 0000000..48e2600
--- /dev/null
+++ b/src/lib/arch/unix/ArchSleepUnix.cpp
@@ -0,0 +1,94 @@
+/*
+ * barrier -- 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 "arch/unix/ArchSleepUnix.h"
+
+#include "arch/Arch.h"
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#if !HAVE_NANOSLEEP
+# if HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif
+# if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+# endif
+# if HAVE_UNISTD_H
+# include <unistd.h>
+# endif
+#endif
+
+//
+// ArchSleepUnix
+//
+
+ArchSleepUnix::ArchSleepUnix()
+{
+ // do nothing
+}
+
+ArchSleepUnix::~ArchSleepUnix()
+{
+ // do nothing
+}
+
+void
+ArchSleepUnix::sleep(double timeout)
+{
+ ARCH->testCancelThread();
+ if (timeout < 0.0) {
+ return;
+ }
+
+#if HAVE_NANOSLEEP
+ // prep timeout
+ struct timespec t;
+ t.tv_sec = (long)timeout;
+ t.tv_nsec = (long)(1.0e+9 * (timeout - (double)t.tv_sec));
+
+ // wait
+ while (nanosleep(&t, &t) < 0)
+ ARCH->testCancelThread();
+#else
+ /* emulate nanosleep() with select() */
+ double startTime = ARCH->time();
+ double timeLeft = timeout;
+ while (timeLeft > 0.0) {
+ struct timeval timeout2;
+ timeout2.tv_sec = static_cast<int>(timeLeft);
+ timeout2.tv_usec = static_cast<int>(1.0e+6 * (timeLeft -
+ timeout2.tv_sec));
+ select((SELECT_TYPE_ARG1) 0,
+ SELECT_TYPE_ARG234 NULL,
+ SELECT_TYPE_ARG234 NULL,
+ SELECT_TYPE_ARG234 NULL,
+ SELECT_TYPE_ARG5 &timeout2);
+ ARCH->testCancelThread();
+ timeLeft = timeout - (ARCH->time() - startTime);
+ }
+#endif
+}
diff --git a/src/lib/arch/unix/ArchSleepUnix.h b/src/lib/arch/unix/ArchSleepUnix.h
new file mode 100644
index 0000000..3e307a5
--- /dev/null
+++ b/src/lib/arch/unix/ArchSleepUnix.h
@@ -0,0 +1,33 @@
+/*
+ * barrier -- 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 "arch/IArchSleep.h"
+
+#define ARCH_SLEEP ArchSleepUnix
+
+//! Unix implementation of IArchSleep
+class ArchSleepUnix : public IArchSleep {
+public:
+ ArchSleepUnix();
+ virtual ~ArchSleepUnix();
+
+ // IArchSleep overrides
+ virtual void sleep(double timeout);
+};
diff --git a/src/lib/arch/unix/ArchStringUnix.cpp b/src/lib/arch/unix/ArchStringUnix.cpp
new file mode 100644
index 0000000..591c826
--- /dev/null
+++ b/src/lib/arch/unix/ArchStringUnix.cpp
@@ -0,0 +1,42 @@
+/*
+ * barrier -- 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 "arch/unix/ArchStringUnix.h"
+
+#include <stdio.h>
+
+//
+// ArchStringUnix
+//
+
+#include "arch/multibyte.h"
+#include "arch/vsnprintf.h"
+
+ArchStringUnix::ArchStringUnix()
+{
+}
+
+ArchStringUnix::~ArchStringUnix()
+{
+}
+
+IArchString::EWideCharEncoding
+ArchStringUnix::getWideCharEncoding()
+{
+ return kUCS4;
+}
diff --git a/src/lib/arch/unix/ArchStringUnix.h b/src/lib/arch/unix/ArchStringUnix.h
new file mode 100644
index 0000000..f7d0035
--- /dev/null
+++ b/src/lib/arch/unix/ArchStringUnix.h
@@ -0,0 +1,34 @@
+/*
+ * barrier -- 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 "arch/IArchString.h"
+
+#define ARCH_STRING ArchStringUnix
+
+//! Unix implementation of IArchString
+class ArchStringUnix : public IArchString {
+public:
+ ArchStringUnix();
+ virtual ~ArchStringUnix();
+
+ // IArchString overrides
+ virtual EWideCharEncoding
+ getWideCharEncoding();
+};
diff --git a/src/lib/arch/unix/ArchSystemUnix.cpp b/src/lib/arch/unix/ArchSystemUnix.cpp
new file mode 100644
index 0000000..f51e47f
--- /dev/null
+++ b/src/lib/arch/unix/ArchSystemUnix.cpp
@@ -0,0 +1,80 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "arch/unix/ArchSystemUnix.h"
+
+#include <sys/utsname.h>
+
+//
+// ArchSystemUnix
+//
+
+ArchSystemUnix::ArchSystemUnix()
+{
+ // do nothing
+}
+
+ArchSystemUnix::~ArchSystemUnix()
+{
+ // do nothing
+}
+
+std::string
+ArchSystemUnix::getOSName() const
+{
+#if defined(HAVE_SYS_UTSNAME_H)
+ struct utsname info;
+ if (uname(&info) == 0) {
+ std::string msg;
+ msg += info.sysname;
+ msg += " ";
+ msg += info.release;
+ return msg;
+ }
+#endif
+ return "Unix";
+}
+
+std::string
+ArchSystemUnix::getPlatformName() const
+{
+#if defined(HAVE_SYS_UTSNAME_H)
+ struct utsname info;
+ if (uname(&info) == 0) {
+ return std::string(info.machine);
+ }
+#endif
+ return "unknown";
+}
+
+std::string
+ArchSystemUnix::setting(const std::string&) const
+{
+ return "";
+}
+
+void
+ArchSystemUnix::setting(const std::string&, const std::string&) const
+{
+}
+
+std::string
+ArchSystemUnix::getLibsUsed(void) const
+{
+ return "not implemented.\nuse lsof on shell";
+}
diff --git a/src/lib/arch/unix/ArchSystemUnix.h b/src/lib/arch/unix/ArchSystemUnix.h
new file mode 100644
index 0000000..aa9c564
--- /dev/null
+++ b/src/lib/arch/unix/ArchSystemUnix.h
@@ -0,0 +1,38 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "arch/IArchSystem.h"
+
+#define ARCH_SYSTEM ArchSystemUnix
+
+//! Unix implementation of IArchString
+class ArchSystemUnix : public IArchSystem {
+public:
+ ArchSystemUnix();
+ virtual ~ArchSystemUnix();
+
+ // IArchSystem overrides
+ virtual std::string getOSName() const;
+ virtual std::string getPlatformName() const;
+ virtual std::string setting(const std::string&) const;
+ virtual void setting(const std::string&, const std::string&) const;
+ virtual std::string getLibsUsed(void) const;
+
+};
diff --git a/src/lib/arch/unix/ArchTaskBarXWindows.cpp b/src/lib/arch/unix/ArchTaskBarXWindows.cpp
new file mode 100644
index 0000000..c3577ad
--- /dev/null
+++ b/src/lib/arch/unix/ArchTaskBarXWindows.cpp
@@ -0,0 +1,51 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "arch/unix/ArchTaskBarXWindows.h"
+
+//
+// ArchTaskBarXWindows
+//
+
+ArchTaskBarXWindows::ArchTaskBarXWindows()
+{
+ // do nothing
+}
+
+ArchTaskBarXWindows::~ArchTaskBarXWindows()
+{
+ // do nothing
+}
+
+void
+ArchTaskBarXWindows::addReceiver(IArchTaskBarReceiver* /*receiver*/)
+{
+ // do nothing
+}
+
+void
+ArchTaskBarXWindows::removeReceiver(IArchTaskBarReceiver* /*receiver*/)
+{
+ // do nothing
+}
+
+void
+ArchTaskBarXWindows::updateReceiver(IArchTaskBarReceiver* /*receiver*/)
+{
+ // do nothing
+}
diff --git a/src/lib/arch/unix/ArchTaskBarXWindows.h b/src/lib/arch/unix/ArchTaskBarXWindows.h
new file mode 100644
index 0000000..f2c8977
--- /dev/null
+++ b/src/lib/arch/unix/ArchTaskBarXWindows.h
@@ -0,0 +1,35 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "arch/IArchTaskBar.h"
+
+#define ARCH_TASKBAR ArchTaskBarXWindows
+
+//! X11 implementation of IArchTaskBar
+class ArchTaskBarXWindows : public IArchTaskBar {
+public:
+ ArchTaskBarXWindows();
+ virtual ~ArchTaskBarXWindows();
+
+ // IArchTaskBar overrides
+ virtual void addReceiver(IArchTaskBarReceiver*);
+ virtual void removeReceiver(IArchTaskBarReceiver*);
+ virtual void updateReceiver(IArchTaskBarReceiver*);
+};
diff --git a/src/lib/arch/unix/ArchTimeUnix.cpp b/src/lib/arch/unix/ArchTimeUnix.cpp
new file mode 100644
index 0000000..24685aa
--- /dev/null
+++ b/src/lib/arch/unix/ArchTimeUnix.cpp
@@ -0,0 +1,52 @@
+/*
+ * barrier -- 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 "arch/unix/ArchTimeUnix.h"
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+//
+// ArchTimeUnix
+//
+
+ArchTimeUnix::ArchTimeUnix()
+{
+ // do nothing
+}
+
+ArchTimeUnix::~ArchTimeUnix()
+{
+ // do nothing
+}
+
+double
+ArchTimeUnix::time()
+{
+ struct timeval t;
+ gettimeofday(&t, NULL);
+ return (double)t.tv_sec + 1.0e-6 * (double)t.tv_usec;
+}
diff --git a/src/lib/arch/unix/ArchTimeUnix.h b/src/lib/arch/unix/ArchTimeUnix.h
new file mode 100644
index 0000000..3c5c0f8
--- /dev/null
+++ b/src/lib/arch/unix/ArchTimeUnix.h
@@ -0,0 +1,33 @@
+/*
+ * barrier -- 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 "arch/IArchTime.h"
+
+#define ARCH_TIME ArchTimeUnix
+
+//! Generic Unix implementation of IArchTime
+class ArchTimeUnix : public IArchTime {
+public:
+ ArchTimeUnix();
+ virtual ~ArchTimeUnix();
+
+ // IArchTime overrides
+ virtual double time();
+};
diff --git a/src/lib/arch/unix/XArchUnix.cpp b/src/lib/arch/unix/XArchUnix.cpp
new file mode 100644
index 0000000..fc7ff65
--- /dev/null
+++ b/src/lib/arch/unix/XArchUnix.cpp
@@ -0,0 +1,32 @@
+/*
+ * barrier -- 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 "arch/unix/XArchUnix.h"
+
+#include <cstring>
+
+//
+// XArchEvalUnix
+//
+
+std::string
+XArchEvalUnix::eval() const
+{
+ // FIXME -- not thread safe
+ return strerror(m_error);
+}
diff --git a/src/lib/arch/unix/XArchUnix.h b/src/lib/arch/unix/XArchUnix.h
new file mode 100644
index 0000000..ae62f4c
--- /dev/null
+++ b/src/lib/arch/unix/XArchUnix.h
@@ -0,0 +1,33 @@
+/*
+ * barrier -- 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 "arch/XArch.h"
+
+//! Lazy error message string evaluation for unix
+class XArchEvalUnix : public XArchEval {
+public:
+ XArchEvalUnix(int error) : m_error(error) { }
+ virtual ~XArchEvalUnix() _NOEXCEPT { }
+
+ virtual std::string eval() const;
+
+private:
+ int m_error;
+};
diff --git a/src/lib/arch/vsnprintf.h b/src/lib/arch/vsnprintf.h
new file mode 100644
index 0000000..5a4e3dc
--- /dev/null
+++ b/src/lib/arch/vsnprintf.h
@@ -0,0 +1,67 @@
+/*
+ * barrier -- 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 "arch/IArchString.h"
+
+#if HAVE_VSNPRINTF
+
+#if !defined(ARCH_VSNPRINTF)
+# define ARCH_VSNPRINTF vsnprintf
+#endif
+
+int
+IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap)
+{
+ int n = ::ARCH_VSNPRINTF(str, size, fmt, ap);
+ if (n > size) {
+ n = -1;
+ }
+ return n;
+}
+
+#elif SYSAPI_UNIX // !HAVE_VSNPRINTF
+
+#include <stdio.h>
+
+int
+IArchString::vsnprintf(char* str, int size, const char* fmt, va_list ap)
+{
+ static FILE* bitbucket = fopen("/dev/null", "w");
+ if (bitbucket == NULL) {
+ // uh oh
+ if (size > 0) {
+ str[0] = '\0';
+ }
+ return 0;
+ }
+ else {
+ // count the characters using the bitbucket
+ int n = vfprintf(bitbucket, fmt, ap);
+ if (n + 1 <= size) {
+ // it'll fit so print it into str
+ vsprintf(str, fmt, ap);
+ }
+ return n;
+ }
+}
+
+#else // !HAVE_VSNPRINTF && !SYSAPI_UNIX
+
+#error vsnprintf not implemented
+
+#endif // !HAVE_VSNPRINTF
diff --git a/src/lib/arch/win32/ArchConsoleWindows.cpp b/src/lib/arch/win32/ArchConsoleWindows.cpp
new file mode 100644
index 0000000..4514555
--- /dev/null
+++ b/src/lib/arch/win32/ArchConsoleWindows.cpp
@@ -0,0 +1,23 @@
+/*
+ * barrier -- 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 "arch/win32/ArchConsoleWindows.h"
+
+ArchConsoleWindows::ArchConsoleWindows() { }
+
+ArchConsoleWindows::~ArchConsoleWindows() { }
diff --git a/src/lib/arch/win32/ArchConsoleWindows.h b/src/lib/arch/win32/ArchConsoleWindows.h
new file mode 100644
index 0000000..f1f0cc9
--- /dev/null
+++ b/src/lib/arch/win32/ArchConsoleWindows.h
@@ -0,0 +1,29 @@
+/*
+ * barrier -- 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 "arch/ArchConsoleStd.h"
+
+#define ARCH_CONSOLE ArchConsoleWindows
+
+class ArchConsoleWindows : public ArchConsoleStd {
+public:
+ ArchConsoleWindows();
+ virtual ~ArchConsoleWindows();
+};
diff --git a/src/lib/arch/win32/ArchDaemonWindows.cpp b/src/lib/arch/win32/ArchDaemonWindows.cpp
new file mode 100644
index 0000000..efcf235
--- /dev/null
+++ b/src/lib/arch/win32/ArchDaemonWindows.cpp
@@ -0,0 +1,704 @@
+/*
+ * barrier -- 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 "arch/win32/ArchDaemonWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/win32/XArchWindows.h"
+#include "arch/Arch.h"
+#include "common/stdvector.h"
+
+#include <sstream>
+
+//
+// ArchDaemonWindows
+//
+
+ArchDaemonWindows* ArchDaemonWindows::s_daemon = NULL;
+
+ArchDaemonWindows::ArchDaemonWindows() :
+m_daemonThreadID(0)
+{
+ m_quitMessage = RegisterWindowMessage("BarrierDaemonExit");
+}
+
+ArchDaemonWindows::~ArchDaemonWindows()
+{
+ // do nothing
+}
+
+int
+ArchDaemonWindows::runDaemon(RunFunc runFunc)
+{
+ assert(s_daemon != NULL);
+ return s_daemon->doRunDaemon(runFunc);
+}
+
+void
+ArchDaemonWindows::daemonRunning(bool running)
+{
+ if (s_daemon != NULL) {
+ s_daemon->doDaemonRunning(running);
+ }
+}
+
+UINT
+ArchDaemonWindows::getDaemonQuitMessage()
+{
+ if (s_daemon != NULL) {
+ return s_daemon->doGetDaemonQuitMessage();
+ }
+ else {
+ return 0;
+ }
+}
+
+void
+ArchDaemonWindows::daemonFailed(int result)
+{
+ assert(s_daemon != NULL);
+ throw XArchDaemonRunFailed(result);
+}
+
+void
+ArchDaemonWindows::installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
+ if (mgr == NULL) {
+ // can't open service manager
+ throw XArchDaemonInstallFailed(new XArchEvalWindows);
+ }
+
+ // create the service
+ SC_HANDLE service = CreateService(
+ mgr,
+ name,
+ name,
+ 0,
+ SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_AUTO_START,
+ SERVICE_ERROR_NORMAL,
+ pathname,
+ NULL,
+ NULL,
+ dependencies,
+ NULL,
+ NULL);
+
+ if (service == NULL) {
+ // can't create service
+ DWORD err = GetLastError();
+ if (err != ERROR_SERVICE_EXISTS) {
+ CloseServiceHandle(mgr);
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+ }
+ else {
+ // done with service (but only try to close if not null)
+ CloseServiceHandle(service);
+ }
+
+ // done with manager
+ CloseServiceHandle(mgr);
+
+ // open the registry key for this service
+ HKEY key = openNTServicesKey();
+ key = ArchMiscWindows::addKey(key, name);
+ if (key == NULL) {
+ // can't open key
+ DWORD err = GetLastError();
+ try {
+ uninstallDaemon(name);
+ }
+ catch (...) {
+ // ignore
+ }
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+
+ // set the description
+ ArchMiscWindows::setValue(key, _T("Description"), description);
+
+ // set command line
+ key = ArchMiscWindows::addKey(key, _T("Parameters"));
+ if (key == NULL) {
+ // can't open key
+ DWORD err = GetLastError();
+ ArchMiscWindows::closeKey(key);
+ try {
+ uninstallDaemon(name);
+ }
+ catch (...) {
+ // ignore
+ }
+ throw XArchDaemonInstallFailed(new XArchEvalWindows(err));
+ }
+ ArchMiscWindows::setValue(key, _T("CommandLine"), commandLine);
+
+ // done with registry
+ ArchMiscWindows::closeKey(key);
+}
+
+void
+ArchDaemonWindows::uninstallDaemon(const char* name)
+{
+ // remove parameters for this service. ignore failures.
+ HKEY key = openNTServicesKey();
+ key = ArchMiscWindows::openKey(key, name);
+ if (key != NULL) {
+ ArchMiscWindows::deleteKey(key, _T("Parameters"));
+ ArchMiscWindows::closeKey(key);
+ }
+
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
+ if (mgr == NULL) {
+ // can't open service manager
+ throw XArchDaemonUninstallFailed(new XArchEvalWindows);
+ }
+
+ // open the service. oddly, you must open a service to delete it.
+ SC_HANDLE service = OpenService(mgr, name, DELETE | SERVICE_STOP);
+ if (service == NULL) {
+ DWORD err = GetLastError();
+ CloseServiceHandle(mgr);
+ if (err != ERROR_SERVICE_DOES_NOT_EXIST) {
+ throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
+ }
+ throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
+ }
+
+ // stop the service. we don't care if we fail.
+ SERVICE_STATUS status;
+ ControlService(service, SERVICE_CONTROL_STOP, &status);
+
+ // delete the service
+ const bool okay = (DeleteService(service) == 0);
+ const DWORD err = GetLastError();
+
+ // clean up
+ CloseServiceHandle(service);
+ CloseServiceHandle(mgr);
+
+ // give windows a chance to remove the service before
+ // we check if it still exists.
+ ARCH->sleep(1);
+
+ // handle failure. ignore error if service isn't installed anymore.
+ if (!okay && isDaemonInstalled(name)) {
+ if (err == ERROR_SUCCESS) {
+ // this seems to occur even though the uninstall was successful.
+ // it could be a timing issue, i.e., isDaemonInstalled is
+ // called too soon. i've added a sleep to try and stop this.
+ return;
+ }
+ if (err == ERROR_IO_PENDING) {
+ // this seems to be a spurious error
+ return;
+ }
+ if (err != ERROR_SERVICE_MARKED_FOR_DELETE) {
+ throw XArchDaemonUninstallFailed(new XArchEvalWindows(err));
+ }
+ throw XArchDaemonUninstallNotInstalled(new XArchEvalWindows(err));
+ }
+}
+
+int
+ArchDaemonWindows::daemonize(const char* name, DaemonFunc func)
+{
+ assert(name != NULL);
+ assert(func != NULL);
+
+ // save daemon function
+ m_daemonFunc = func;
+
+ // construct the service entry
+ SERVICE_TABLE_ENTRY entry[2];
+ entry[0].lpServiceName = const_cast<char*>(name);
+ entry[0].lpServiceProc = &ArchDaemonWindows::serviceMainEntry;
+ entry[1].lpServiceName = NULL;
+ entry[1].lpServiceProc = NULL;
+
+ // hook us up to the service control manager. this won't return
+ // (if successful) until the processes have terminated.
+ s_daemon = this;
+ if (StartServiceCtrlDispatcher(entry) == 0) {
+ // StartServiceCtrlDispatcher failed
+ s_daemon = NULL;
+ throw XArchDaemonFailed(new XArchEvalWindows);
+ }
+
+ s_daemon = NULL;
+ return m_daemonResult;
+}
+
+bool
+ArchDaemonWindows::canInstallDaemon(const char* /*name*/)
+{
+ // check if we can open service manager for write
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_WRITE);
+ if (mgr == NULL) {
+ return false;
+ }
+ CloseServiceHandle(mgr);
+
+ // check if we can open the registry key
+ HKEY key = openNTServicesKey();
+ ArchMiscWindows::closeKey(key);
+
+ return (key != NULL);
+}
+
+bool
+ArchDaemonWindows::isDaemonInstalled(const char* name)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
+ if (mgr == NULL) {
+ return false;
+ }
+
+ // open the service
+ SC_HANDLE service = OpenService(mgr, name, GENERIC_READ);
+
+ // clean up
+ if (service != NULL) {
+ CloseServiceHandle(service);
+ }
+ CloseServiceHandle(mgr);
+
+ return (service != NULL);
+}
+
+HKEY
+ArchDaemonWindows::openNTServicesKey()
+{
+ static const char* s_keyNames[] = {
+ _T("SYSTEM"),
+ _T("CurrentControlSet"),
+ _T("Services"),
+ NULL
+ };
+
+ return ArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
+}
+
+bool
+ArchDaemonWindows::isRunState(DWORD state)
+{
+ switch (state) {
+ case SERVICE_START_PENDING:
+ case SERVICE_CONTINUE_PENDING:
+ case SERVICE_RUNNING:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+int
+ArchDaemonWindows::doRunDaemon(RunFunc run)
+{
+ // should only be called from DaemonFunc
+ assert(m_serviceMutex != NULL);
+ assert(run != NULL);
+
+ // create message queue for this thread
+ MSG dummy;
+ PeekMessage(&dummy, NULL, 0, 0, PM_NOREMOVE);
+
+ int result = 0;
+ ARCH->lockMutex(m_serviceMutex);
+ m_daemonThreadID = GetCurrentThreadId();
+ while (m_serviceState != SERVICE_STOPPED) {
+ // wait until we're told to start
+ while (!isRunState(m_serviceState) &&
+ m_serviceState != SERVICE_STOP_PENDING) {
+ ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
+ }
+
+ // run unless told to stop
+ if (m_serviceState != SERVICE_STOP_PENDING) {
+ ARCH->unlockMutex(m_serviceMutex);
+ try {
+ result = run();
+ }
+ catch (...) {
+ ARCH->lockMutex(m_serviceMutex);
+ setStatusError(0);
+ m_serviceState = SERVICE_STOPPED;
+ setStatus(m_serviceState);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ ARCH->unlockMutex(m_serviceMutex);
+ throw;
+ }
+ ARCH->lockMutex(m_serviceMutex);
+ }
+
+ // notify of new state
+ if (m_serviceState == SERVICE_PAUSE_PENDING) {
+ m_serviceState = SERVICE_PAUSED;
+ }
+ else {
+ m_serviceState = SERVICE_STOPPED;
+ }
+ setStatus(m_serviceState);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ }
+ ARCH->unlockMutex(m_serviceMutex);
+ return result;
+}
+
+void
+ArchDaemonWindows::doDaemonRunning(bool running)
+{
+ ARCH->lockMutex(m_serviceMutex);
+ if (running) {
+ m_serviceState = SERVICE_RUNNING;
+ setStatus(m_serviceState);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ }
+ ARCH->unlockMutex(m_serviceMutex);
+}
+
+UINT
+ArchDaemonWindows::doGetDaemonQuitMessage()
+{
+ return m_quitMessage;
+}
+
+void
+ArchDaemonWindows::setStatus(DWORD state)
+{
+ setStatus(state, 0, 0);
+}
+
+void
+ArchDaemonWindows::setStatus(DWORD state, DWORD step, DWORD waitHint)
+{
+ assert(s_daemon != NULL);
+
+ SERVICE_STATUS status;
+ status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
+ SERVICE_INTERACTIVE_PROCESS;
+ status.dwCurrentState = state;
+ status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+ status.dwWin32ExitCode = NO_ERROR;
+ status.dwServiceSpecificExitCode = 0;
+ status.dwCheckPoint = step;
+ status.dwWaitHint = waitHint;
+ SetServiceStatus(s_daemon->m_statusHandle, &status);
+}
+
+void
+ArchDaemonWindows::setStatusError(DWORD error)
+{
+ assert(s_daemon != NULL);
+
+ SERVICE_STATUS status;
+ status.dwServiceType = SERVICE_WIN32_OWN_PROCESS |
+ SERVICE_INTERACTIVE_PROCESS;
+ status.dwCurrentState = SERVICE_STOPPED;
+ status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+ status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
+ status.dwServiceSpecificExitCode = error;
+ status.dwCheckPoint = 0;
+ status.dwWaitHint = 0;
+ SetServiceStatus(s_daemon->m_statusHandle, &status);
+}
+
+void
+ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR* argvIn)
+{
+ typedef std::vector<LPCTSTR> ArgList;
+ typedef std::vector<std::string> Arguments;
+ const char** argv = const_cast<const char**>(argvIn);
+
+ // create synchronization objects
+ m_serviceMutex = ARCH->newMutex();
+ m_serviceCondVar = ARCH->newCondVar();
+
+ // register our service handler function
+ m_statusHandle = RegisterServiceCtrlHandler(argv[0],
+ &ArchDaemonWindows::serviceHandlerEntry);
+ if (m_statusHandle == 0) {
+ // cannot start as service
+ m_daemonResult = -1;
+ ARCH->closeCondVar(m_serviceCondVar);
+ ARCH->closeMutex(m_serviceMutex);
+ return;
+ }
+
+ // tell service control manager that we're starting
+ m_serviceState = SERVICE_START_PENDING;
+ setStatus(m_serviceState, 0, 10000);
+
+ std::string commandLine;
+
+ // if no arguments supplied then try getting them from the registry.
+ // the first argument doesn't count because it's the service name.
+ Arguments args;
+ ArgList myArgv;
+ if (argc <= 1) {
+ // read command line
+ HKEY key = openNTServicesKey();
+ key = ArchMiscWindows::openKey(key, argvIn[0]);
+ key = ArchMiscWindows::openKey(key, _T("Parameters"));
+ if (key != NULL) {
+ commandLine = ArchMiscWindows::readValueString(key,
+ _T("CommandLine"));
+ }
+
+ // if the command line isn't empty then parse and use it
+ if (!commandLine.empty()) {
+ // parse, honoring double quoted substrings
+ std::string::size_type i = commandLine.find_first_not_of(" \t");
+ while (i != std::string::npos && i != commandLine.size()) {
+ // find end of string
+ std::string::size_type e;
+ if (commandLine[i] == '\"') {
+ // quoted. find closing quote.
+ ++i;
+ e = commandLine.find("\"", i);
+
+ // whitespace must follow closing quote
+ if (e == std::string::npos ||
+ (e + 1 != commandLine.size() &&
+ commandLine[e + 1] != ' ' &&
+ commandLine[e + 1] != '\t')) {
+ args.clear();
+ break;
+ }
+
+ // extract
+ args.push_back(commandLine.substr(i, e - i));
+ i = e + 1;
+ }
+ else {
+ // unquoted. find next whitespace.
+ e = commandLine.find_first_of(" \t", i);
+ if (e == std::string::npos) {
+ e = commandLine.size();
+ }
+
+ // extract
+ args.push_back(commandLine.substr(i, e - i));
+ i = e + 1;
+ }
+
+ // next argument
+ i = commandLine.find_first_not_of(" \t", i);
+ }
+
+ // service name goes first
+ myArgv.push_back(argv[0]);
+
+ // get pointers
+ for (size_t j = 0; j < args.size(); ++j) {
+ myArgv.push_back(args[j].c_str());
+ }
+
+ // adjust argc/argv
+ argc = (DWORD)myArgv.size();
+ argv = &myArgv[0];
+ }
+ }
+
+ m_commandLine = commandLine;
+
+ try {
+ // invoke daemon function
+ m_daemonResult = m_daemonFunc(static_cast<int>(argc), argv);
+ }
+ catch (XArchDaemonRunFailed& e) {
+ setStatusError(e.m_result);
+ m_daemonResult = -1;
+ }
+ catch (...) {
+ setStatusError(1);
+ m_daemonResult = -1;
+ }
+
+ // clean up
+ ARCH->closeCondVar(m_serviceCondVar);
+ ARCH->closeMutex(m_serviceMutex);
+
+ // we're going to exit now, so set status to stopped
+ m_serviceState = SERVICE_STOPPED;
+ setStatus(m_serviceState, 0, 10000);
+}
+
+void WINAPI
+ArchDaemonWindows::serviceMainEntry(DWORD argc, LPTSTR* argv)
+{
+ s_daemon->serviceMain(argc, argv);
+}
+
+void
+ArchDaemonWindows::serviceHandler(DWORD ctrl)
+{
+ assert(m_serviceMutex != NULL);
+ assert(m_serviceCondVar != NULL);
+
+ ARCH->lockMutex(m_serviceMutex);
+
+ // ignore request if service is already stopped
+ if (s_daemon == NULL || m_serviceState == SERVICE_STOPPED) {
+ if (s_daemon != NULL) {
+ setStatus(m_serviceState);
+ }
+ ARCH->unlockMutex(m_serviceMutex);
+ return;
+ }
+
+ switch (ctrl) {
+ case SERVICE_CONTROL_PAUSE:
+ m_serviceState = SERVICE_PAUSE_PENDING;
+ setStatus(m_serviceState, 0, 5000);
+ PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
+ while (isRunState(m_serviceState)) {
+ ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
+ }
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ // FIXME -- maybe should flush quit messages from queue
+ m_serviceState = SERVICE_CONTINUE_PENDING;
+ setStatus(m_serviceState, 0, 5000);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ break;
+
+ case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
+ m_serviceState = SERVICE_STOP_PENDING;
+ setStatus(m_serviceState, 0, 5000);
+ PostThreadMessage(m_daemonThreadID, m_quitMessage, 0, 0);
+ ARCH->broadcastCondVar(m_serviceCondVar);
+ while (isRunState(m_serviceState)) {
+ ARCH->waitCondVar(m_serviceCondVar, m_serviceMutex, -1.0);
+ }
+ break;
+
+ default:
+ // unknown service command
+ // fall through
+
+ case SERVICE_CONTROL_INTERROGATE:
+ setStatus(m_serviceState);
+ break;
+ }
+
+ ARCH->unlockMutex(m_serviceMutex);
+}
+
+void WINAPI
+ArchDaemonWindows::serviceHandlerEntry(DWORD ctrl)
+{
+ s_daemon->serviceHandler(ctrl);
+}
+
+void
+ArchDaemonWindows::start(const char* name)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
+ if (mgr == NULL) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // open the service
+ SC_HANDLE service = OpenService(
+ mgr, name, SERVICE_START);
+
+ if (service == NULL) {
+ CloseServiceHandle(mgr);
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // start the service
+ if (!StartService(service, 0, NULL)) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+}
+
+void
+ArchDaemonWindows::stop(const char* name)
+{
+ // open service manager
+ SC_HANDLE mgr = OpenSCManager(NULL, NULL, GENERIC_READ);
+ if (mgr == NULL) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // open the service
+ SC_HANDLE service = OpenService(
+ mgr, name,
+ SERVICE_STOP | SERVICE_QUERY_STATUS);
+
+ if (service == NULL) {
+ CloseServiceHandle(mgr);
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+
+ // ask the service to stop, asynchronously
+ SERVICE_STATUS ss;
+ if (!ControlService(service, SERVICE_CONTROL_STOP, &ss)) {
+ DWORD dwErrCode = GetLastError();
+ if (dwErrCode != ERROR_SERVICE_NOT_ACTIVE) {
+ throw XArchDaemonFailed(new XArchEvalWindows());
+ }
+ }
+}
+
+void
+ArchDaemonWindows::installDaemon()
+{
+ // install default daemon if not already installed.
+ if (!isDaemonInstalled(DEFAULT_DAEMON_NAME)) {
+ char path[MAX_PATH];
+ GetModuleFileName(ArchMiscWindows::instanceWin32(), path, MAX_PATH);
+
+ // wrap in quotes so a malicious user can't start \Program.exe as admin.
+ std::stringstream ss;
+ ss << '"';
+ ss << path;
+ ss << '"';
+
+ installDaemon(DEFAULT_DAEMON_NAME, DEFAULT_DAEMON_INFO, ss.str().c_str(), "", "");
+ }
+
+ start(DEFAULT_DAEMON_NAME);
+}
+
+void
+ArchDaemonWindows::uninstallDaemon()
+{
+ // remove service if installed.
+ if (isDaemonInstalled(DEFAULT_DAEMON_NAME)) {
+ uninstallDaemon(DEFAULT_DAEMON_NAME);
+ }
+}
diff --git a/src/lib/arch/win32/ArchDaemonWindows.h b/src/lib/arch/win32/ArchDaemonWindows.h
new file mode 100644
index 0000000..2db9792
--- /dev/null
+++ b/src/lib/arch/win32/ArchDaemonWindows.h
@@ -0,0 +1,151 @@
+/*
+ * barrier -- 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 "arch/IArchDaemon.h"
+#include "arch/IArchMultithread.h"
+#include "common/stdstring.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <tchar.h>
+
+#define ARCH_DAEMON ArchDaemonWindows
+
+//! Win32 implementation of IArchDaemon
+class ArchDaemonWindows : public IArchDaemon {
+public:
+ typedef int (*RunFunc)(void);
+
+ ArchDaemonWindows();
+ virtual ~ArchDaemonWindows();
+
+ //! Run the daemon
+ /*!
+ When the client calls \c daemonize(), the \c DaemonFunc should call this
+ function after initialization and argument parsing to perform the
+ daemon processing. The \c runFunc should perform the daemon's
+ main loop, calling \c daemonRunning(true) when it enters the main loop
+ (i.e. after initialization) and \c daemonRunning(false) when it leaves
+ the main loop. The \c runFunc is called in a new thread and when the
+ daemon must exit the main loop due to some external control the
+ getDaemonQuitMessage() is posted to the thread. This function returns
+ what \c runFunc returns. \c runFunc should call \c daemonFailed() if
+ the daemon fails.
+ */
+ static int runDaemon(RunFunc runFunc);
+
+ //! Indicate daemon is in main loop
+ /*!
+ The \c runFunc passed to \c runDaemon() should call this function
+ to indicate when it has entered (\c running is \c true) or exited
+ (\c running is \c false) the main loop.
+ */
+ static void daemonRunning(bool running);
+
+ //! Indicate failure of running daemon
+ /*!
+ The \c runFunc passed to \c runDaemon() should call this function
+ to indicate failure. \c result is returned by \c daemonize().
+ */
+ static void daemonFailed(int result);
+
+ //! Get daemon quit message
+ /*!
+ The windows NT daemon tells daemon thread to exit by posting this
+ message to it. The thread must, of course, have a message queue
+ for this to work.
+ */
+ static UINT getDaemonQuitMessage();
+
+ // IArchDaemon overrides
+ virtual void installDaemon(const char* name,
+ const char* description,
+ const char* pathname,
+ const char* commandLine,
+ const char* dependencies);
+ virtual void uninstallDaemon(const char* name);
+ virtual void installDaemon();
+ virtual void uninstallDaemon();
+ virtual int daemonize(const char* name, DaemonFunc func);
+ virtual bool canInstallDaemon(const char* name);
+ virtual bool isDaemonInstalled(const char* name);
+ std::string commandLine() const { return m_commandLine; }
+
+private:
+ static HKEY openNTServicesKey();
+
+ int doRunDaemon(RunFunc runFunc);
+ void doDaemonRunning(bool running);
+ UINT doGetDaemonQuitMessage();
+
+ static void setStatus(DWORD state);
+ static void setStatus(DWORD state, DWORD step, DWORD waitHint);
+ static void setStatusError(DWORD error);
+
+ static bool isRunState(DWORD state);
+
+ void serviceMain(DWORD, LPTSTR*);
+ static void WINAPI serviceMainEntry(DWORD, LPTSTR*);
+
+ void serviceHandler(DWORD ctrl);
+ static void WINAPI serviceHandlerEntry(DWORD ctrl);
+
+ void start(const char* name);
+ void stop(const char* name);
+
+private:
+ class XArchDaemonRunFailed {
+ public:
+ XArchDaemonRunFailed(int result) : m_result(result) { }
+
+ public:
+ int m_result;
+ };
+
+private:
+ static ArchDaemonWindows* s_daemon;
+
+ ArchMutex m_serviceMutex;
+ ArchCond m_serviceCondVar;
+ DWORD m_serviceState;
+ bool m_serviceHandlerWaiting;
+ bool m_serviceRunning;
+
+ DWORD m_daemonThreadID;
+ DaemonFunc m_daemonFunc;
+ int m_daemonResult;
+
+ SERVICE_STATUS_HANDLE m_statusHandle;
+
+ UINT m_quitMessage;
+
+ std::string m_commandLine;
+};
+
+#define DEFAULT_DAEMON_NAME _T("Barrier")
+#define DEFAULT_DAEMON_INFO _T("Manages the Barrier foreground processes.")
+
+static const TCHAR* const g_daemonKeyPath[] = {
+ _T("SOFTWARE"),
+ _T("The Barrier Project"),
+ _T("Barrier"),
+ _T("Service"),
+ NULL
+};
diff --git a/src/lib/arch/win32/ArchFileWindows.cpp b/src/lib/arch/win32/ArchFileWindows.cpp
new file mode 100644
index 0000000..53b4b59
--- /dev/null
+++ b/src/lib/arch/win32/ArchFileWindows.cpp
@@ -0,0 +1,203 @@
+/*
+ * barrier -- 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 "arch/win32/ArchFileWindows.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <shlobj.h>
+#include <tchar.h>
+#include <string.h>
+
+//
+// ArchFileWindows
+//
+
+ArchFileWindows::ArchFileWindows()
+{
+ // do nothing
+}
+
+ArchFileWindows::~ArchFileWindows()
+{
+ // do nothing
+}
+
+const char*
+ArchFileWindows::getBasename(const char* pathname)
+{
+ if (pathname == NULL) {
+ return NULL;
+ }
+
+ // check for last slash
+ const char* basename = strrchr(pathname, '/');
+ if (basename != NULL) {
+ ++basename;
+ }
+ else {
+ basename = pathname;
+ }
+
+ // check for last backslash
+ const char* basename2 = strrchr(pathname, '\\');
+ if (basename2 != NULL && basename2 > basename) {
+ basename = basename2 + 1;
+ }
+
+ return basename;
+}
+
+std::string
+ArchFileWindows::getUserDirectory()
+{
+ // try %HOMEPATH%
+ TCHAR dir[MAX_PATH];
+ DWORD size = sizeof(dir) / sizeof(TCHAR);
+ DWORD result = GetEnvironmentVariable(_T("HOMEPATH"), dir, size);
+ if (result != 0 && result <= size) {
+ // sanity check -- if dir doesn't appear to start with a
+ // drive letter and isn't a UNC name then don't use it
+ // FIXME -- allow UNC names
+ if (dir[0] != '\0' && (dir[1] == ':' ||
+ ((dir[0] == '\\' || dir[0] == '/') &&
+ (dir[1] == '\\' || dir[1] == '/')))) {
+ return dir;
+ }
+ }
+
+ // get the location of the personal files. that's as close to
+ // a home directory as we're likely to find.
+ ITEMIDLIST* idl;
+ if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &idl))) {
+ TCHAR* path = NULL;
+ if (SHGetPathFromIDList(idl, dir)) {
+ DWORD attr = GetFileAttributes(dir);
+ if (attr != 0xffffffff && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ path = dir;
+ }
+
+ IMalloc* shalloc;
+ if (SUCCEEDED(SHGetMalloc(&shalloc))) {
+ shalloc->Free(idl);
+ shalloc->Release();
+ }
+
+ if (path != NULL) {
+ return path;
+ }
+ }
+
+ // use root of C drive as a default
+ return "C:";
+}
+
+std::string
+ArchFileWindows::getSystemDirectory()
+{
+ // get windows directory
+ char dir[MAX_PATH];
+ if (GetWindowsDirectory(dir, sizeof(dir)) != 0) {
+ return dir;
+ }
+ else {
+ // can't get it. use C:\ as a default.
+ return "C:";
+ }
+}
+
+std::string
+ArchFileWindows::getInstalledDirectory()
+{
+ char fileNameBuffer[MAX_PATH];
+ GetModuleFileName(NULL, fileNameBuffer, MAX_PATH);
+ std::string fileName(fileNameBuffer);
+ size_t lastSlash = fileName.find_last_of("\\");
+ fileName = fileName.substr(0, lastSlash);
+
+ return fileName;
+}
+
+std::string
+ArchFileWindows::getLogDirectory()
+{
+ return getInstalledDirectory();
+}
+
+std::string
+ArchFileWindows::getPluginDirectory()
+{
+ if (!m_pluginDirectory.empty()) {
+ return m_pluginDirectory;
+ }
+
+ std::string dir = getProfileDirectory();
+ dir.append("\\Plugins");
+ return dir;
+}
+
+std::string
+ArchFileWindows::getProfileDirectory()
+{
+ String dir;
+ if (!m_profileDirectory.empty()) {
+ dir = m_profileDirectory;
+ }
+ else {
+ TCHAR result[MAX_PATH];
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, result))) {
+ dir = result;
+ }
+ else {
+ dir = getUserDirectory();
+ }
+ }
+
+ // HACK: append program name, this seems wrong.
+ dir.append("\\Barrier");
+
+ return dir;
+}
+
+std::string
+ArchFileWindows::concatPath(const std::string& prefix,
+ const std::string& suffix)
+{
+ std::string path;
+ path.reserve(prefix.size() + 1 + suffix.size());
+ path += prefix;
+ if (path.size() == 0 ||
+ (path[path.size() - 1] != '\\' &&
+ path[path.size() - 1] != '/')) {
+ path += '\\';
+ }
+ path += suffix;
+ return path;
+}
+
+void
+ArchFileWindows::setProfileDirectory(const String& s)
+{
+ m_profileDirectory = s;
+}
+
+void
+ArchFileWindows::setPluginDirectory(const String& s)
+{
+ m_pluginDirectory = s;
+}
diff --git a/src/lib/arch/win32/ArchFileWindows.h b/src/lib/arch/win32/ArchFileWindows.h
new file mode 100644
index 0000000..4747b9c
--- /dev/null
+++ b/src/lib/arch/win32/ArchFileWindows.h
@@ -0,0 +1,47 @@
+/*
+ * barrier -- 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 "arch/IArchFile.h"
+
+#define ARCH_FILE ArchFileWindows
+
+//! Win32 implementation of IArchFile
+class ArchFileWindows : public IArchFile {
+public:
+ ArchFileWindows();
+ virtual ~ArchFileWindows();
+
+ // IArchFile overrides
+ virtual const char* getBasename(const char* pathname);
+ virtual std::string getUserDirectory();
+ virtual std::string getSystemDirectory();
+ virtual std::string getInstalledDirectory();
+ virtual std::string getLogDirectory();
+ virtual std::string getPluginDirectory();
+ virtual std::string getProfileDirectory();
+ virtual std::string concatPath(const std::string& prefix,
+ const std::string& suffix);
+ virtual void setProfileDirectory(const String& s);
+ virtual void setPluginDirectory(const String& s);
+
+private:
+ String m_profileDirectory;
+ String m_pluginDirectory;
+};
diff --git a/src/lib/arch/win32/ArchInternetWindows.cpp b/src/lib/arch/win32/ArchInternetWindows.cpp
new file mode 100644
index 0000000..7f69c7f
--- /dev/null
+++ b/src/lib/arch/win32/ArchInternetWindows.cpp
@@ -0,0 +1,224 @@
+/*
+ * barrier -- 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 "arch/win32/ArchInternetWindows.h"
+#include "arch/win32/XArchWindows.h"
+#include "arch/Arch.h"
+#include "common/Version.h"
+
+#include <sstream>
+#include <Wininet.h>
+#include <Shlwapi.h>
+
+struct WinINetUrl {
+ String m_scheme;
+ String m_host;
+ String m_path;
+ INTERNET_PORT m_port;
+ DWORD m_flags;
+};
+
+class WinINetRequest {
+public:
+ WinINetRequest(const String& url);
+ ~WinINetRequest();
+
+ String send();
+ void openSession();
+ void connect();
+ void openRequest();
+
+private:
+ HINTERNET m_session;
+ HINTERNET m_connect;
+ HINTERNET m_request;
+ WinINetUrl m_url;
+ bool m_used;
+};
+
+//
+// ArchInternetWindows
+//
+
+String
+ArchInternetWindows::get(const String& url)
+{
+ WinINetRequest request(url);
+ return request.send();
+}
+
+String
+ArchInternetWindows::urlEncode(const String& url)
+{
+ TCHAR buffer[1024];
+ DWORD bufferSize = sizeof(buffer);
+
+ if (UrlEscape(url.c_str(), buffer, &bufferSize, URL_ESCAPE_UNSAFE) != S_OK) {
+ throw XArch(new XArchEvalWindows());
+ }
+
+ String result(buffer);
+
+ // the win32 url encoding funcitons are pretty useless (to us) and only
+ // escape "unsafe" chars, but not + or =, so we need to replace these
+ // manually (and probably many other chars).
+ barrier::string::findReplaceAll(result, "+", "%2B");
+ barrier::string::findReplaceAll(result, "=", "%3D");
+
+ return result;
+}
+
+//
+// WinINetRequest
+//
+
+static WinINetUrl parseUrl(const String& url);
+
+WinINetRequest::WinINetRequest(const String& url) :
+ m_session(NULL),
+ m_connect(NULL),
+ m_request(NULL),
+ m_used(false),
+ m_url(parseUrl(url))
+{
+}
+
+WinINetRequest::~WinINetRequest()
+{
+ if (m_request != NULL) {
+ InternetCloseHandle(m_request);
+ }
+
+ if (m_connect != NULL) {
+ InternetCloseHandle(m_connect);
+ }
+
+ if (m_session != NULL) {
+ InternetCloseHandle(m_session);
+ }
+}
+
+String
+WinINetRequest::send()
+{
+ if (m_used) {
+ throw XArch("class is one time use.");
+ }
+ m_used = true;
+
+ openSession();
+ connect();
+ openRequest();
+
+ String headers("Content-Type: text/html");
+ if (!HttpSendRequest(m_request, headers.c_str(), (DWORD)headers.length(), NULL, NULL)) {
+ throw XArch(new XArchEvalWindows());
+ }
+
+ std::stringstream result;
+ CHAR buffer[1025];
+ DWORD read = 0;
+
+ while (InternetReadFile(m_request, buffer, sizeof(buffer) - 1, &read) && (read != 0)) {
+ buffer[read] = 0;
+ result << buffer;
+ read = 0;
+ }
+
+ return result.str();
+}
+
+void
+WinINetRequest::openSession()
+{
+ std::stringstream userAgent;
+ userAgent << "Barrier ";
+ userAgent << kVersion;
+
+ m_session = InternetOpen(
+ userAgent.str().c_str(),
+ INTERNET_OPEN_TYPE_PRECONFIG,
+ NULL,
+ NULL,
+ NULL);
+
+ if (m_session == NULL) {
+ throw XArch(new XArchEvalWindows());
+ }
+}
+
+void
+WinINetRequest::connect()
+{
+ m_connect = InternetConnect(
+ m_session,
+ m_url.m_host.c_str(),
+ m_url.m_port,
+ NULL,
+ NULL,
+ INTERNET_SERVICE_HTTP,
+ NULL,
+ NULL);
+
+ if (m_connect == NULL) {
+ throw XArch(new XArchEvalWindows());
+ }
+}
+
+void
+WinINetRequest::openRequest()
+{
+ m_request = HttpOpenRequest(
+ m_connect,
+ "GET",
+ m_url.m_path.c_str(),
+ HTTP_VERSION,
+ NULL,
+ NULL,
+ m_url.m_flags,
+ NULL);
+
+ if (m_request == NULL) {
+ throw XArch(new XArchEvalWindows());
+ }
+}
+
+// nb: i tried to use InternetCrackUrl here, but couldn't quite get that to
+// work. here's some (less robust) code to split the url into components.
+// this works fine with simple urls, but doesn't consider the full url spec.
+static WinINetUrl
+parseUrl(const String& url)
+{
+ WinINetUrl parsed;
+
+ size_t schemeEnd = url.find("://");
+ size_t hostEnd = url.find('/', schemeEnd + 3);
+
+ parsed.m_scheme = url.substr(0, schemeEnd);
+ parsed.m_host = url.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
+ parsed.m_path = url.substr(hostEnd);
+
+ parsed.m_port = INTERNET_DEFAULT_HTTP_PORT;
+ parsed.m_flags = 0;
+
+ if (parsed.m_scheme.find("https") != String::npos) {
+ parsed.m_port = INTERNET_DEFAULT_HTTPS_PORT;
+ parsed.m_flags = INTERNET_FLAG_SECURE;
+ }
+
+ return parsed;
+}
diff --git a/src/lib/arch/win32/ArchInternetWindows.h b/src/lib/arch/win32/ArchInternetWindows.h
new file mode 100644
index 0000000..bab8d3c
--- /dev/null
+++ b/src/lib/arch/win32/ArchInternetWindows.h
@@ -0,0 +1,28 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#define ARCH_INTERNET ArchInternetWindows
+
+#include "base/String.h"
+
+class ArchInternetWindows {
+public:
+ String get(const String& url);
+ String urlEncode(const String& url);
+};
diff --git a/src/lib/arch/win32/ArchLogWindows.cpp b/src/lib/arch/win32/ArchLogWindows.cpp
new file mode 100644
index 0000000..bc17abf
--- /dev/null
+++ b/src/lib/arch/win32/ArchLogWindows.cpp
@@ -0,0 +1,95 @@
+/*
+ * barrier -- 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 "arch/win32/ArchLogWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+
+#include <string.h>
+
+//
+// ArchLogWindows
+//
+
+ArchLogWindows::ArchLogWindows() : m_eventLog(NULL)
+{
+ // do nothing
+}
+
+ArchLogWindows::~ArchLogWindows()
+{
+ // do nothing
+}
+
+void
+ArchLogWindows::openLog(const char* name)
+{
+ if (m_eventLog == NULL) {
+ m_eventLog = RegisterEventSource(NULL, name);
+ }
+}
+
+void
+ArchLogWindows::closeLog()
+{
+ if (m_eventLog != NULL) {
+ DeregisterEventSource(m_eventLog);
+ m_eventLog = NULL;
+ }
+}
+
+void
+ArchLogWindows::showLog(bool)
+{
+ // do nothing
+}
+
+void
+ArchLogWindows::writeLog(ELevel level, const char* msg)
+{
+ if (m_eventLog != NULL) {
+ // convert priority
+ WORD type;
+ switch (level) {
+ case kERROR:
+ type = EVENTLOG_ERROR_TYPE;
+ break;
+
+ case kWARNING:
+ type = EVENTLOG_WARNING_TYPE;
+ break;
+
+ default:
+ type = EVENTLOG_INFORMATION_TYPE;
+ break;
+ }
+
+ // log it
+ // FIXME -- win32 wants to use a message table to look up event
+ // strings. log messages aren't organized that way so we'll
+ // just dump our string into the raw data section of the event
+ // so users can at least see the message. note that we use our
+ // level as the event category.
+ ReportEvent(m_eventLog, type, static_cast<WORD>(level),
+ 0, // event ID
+ NULL,
+ 0,
+ (DWORD)strlen(msg) + 1, // raw data size
+ NULL,
+ const_cast<char*>(msg));// raw data
+ }
+}
diff --git a/src/lib/arch/win32/ArchLogWindows.h b/src/lib/arch/win32/ArchLogWindows.h
new file mode 100644
index 0000000..3a997f1
--- /dev/null
+++ b/src/lib/arch/win32/ArchLogWindows.h
@@ -0,0 +1,42 @@
+/*
+ * barrier -- 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 "arch/IArchLog.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#define ARCH_LOG ArchLogWindows
+
+//! Win32 implementation of IArchLog
+class ArchLogWindows : public IArchLog {
+public:
+ ArchLogWindows();
+ virtual ~ArchLogWindows();
+
+ // IArchLog overrides
+ virtual void openLog(const char* name);
+ virtual void closeLog();
+ virtual void showLog(bool showIfEmpty);
+ virtual void writeLog(ELevel, const char*);
+
+private:
+ HANDLE m_eventLog;
+};
diff --git a/src/lib/arch/win32/ArchMiscWindows.cpp b/src/lib/arch/win32/ArchMiscWindows.cpp
new file mode 100644
index 0000000..924afa2
--- /dev/null
+++ b/src/lib/arch/win32/ArchMiscWindows.cpp
@@ -0,0 +1,524 @@
+/*
+ * barrier -- 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 "arch/win32/ArchMiscWindows.h"
+#include "arch/win32/ArchDaemonWindows.h"
+#include "base/Log.h"
+#include "common/Version.h"
+
+#include <Wtsapi32.h>
+#pragma warning(disable: 4099)
+#include <Userenv.h>
+#pragma warning(default: 4099)
+
+// parent process name for services in Vista
+#define SERVICE_LAUNCHER "services.exe"
+
+#ifndef ES_SYSTEM_REQUIRED
+#define ES_SYSTEM_REQUIRED ((DWORD)0x00000001)
+#endif
+#ifndef ES_DISPLAY_REQUIRED
+#define ES_DISPLAY_REQUIRED ((DWORD)0x00000002)
+#endif
+#ifndef ES_CONTINUOUS
+#define ES_CONTINUOUS ((DWORD)0x80000000)
+#endif
+typedef DWORD EXECUTION_STATE;
+
+//
+// ArchMiscWindows
+//
+
+ArchMiscWindows::Dialogs* ArchMiscWindows::s_dialogs = NULL;
+DWORD ArchMiscWindows::s_busyState = 0;
+ArchMiscWindows::STES_t ArchMiscWindows::s_stes = NULL;
+HICON ArchMiscWindows::s_largeIcon = NULL;
+HICON ArchMiscWindows::s_smallIcon = NULL;
+HINSTANCE ArchMiscWindows::s_instanceWin32 = NULL;
+
+void
+ArchMiscWindows::cleanup()
+{
+ delete s_dialogs;
+}
+
+void
+ArchMiscWindows::init()
+{
+ // stop windows system error dialogs from showing.
+ SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ s_dialogs = new Dialogs;
+}
+
+void
+ArchMiscWindows::setIcons(HICON largeIcon, HICON smallIcon)
+{
+ s_largeIcon = largeIcon;
+ s_smallIcon = smallIcon;
+}
+
+void
+ArchMiscWindows::getIcons(HICON& largeIcon, HICON& smallIcon)
+{
+ largeIcon = s_largeIcon;
+ smallIcon = s_smallIcon;
+}
+
+int
+ArchMiscWindows::runDaemon(RunFunc runFunc)
+{
+ return ArchDaemonWindows::runDaemon(runFunc);
+}
+
+void
+ArchMiscWindows::daemonRunning(bool running)
+{
+ ArchDaemonWindows::daemonRunning(running);
+}
+
+void
+ArchMiscWindows::daemonFailed(int result)
+{
+ ArchDaemonWindows::daemonFailed(result);
+}
+
+UINT
+ArchMiscWindows::getDaemonQuitMessage()
+{
+ return ArchDaemonWindows::getDaemonQuitMessage();
+}
+
+HKEY
+ArchMiscWindows::openKey(HKEY key, const TCHAR* keyName)
+{
+ return openKey(key, keyName, false);
+}
+
+HKEY
+ArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames)
+{
+ return openKey(key, keyNames, false);
+}
+
+HKEY
+ArchMiscWindows::addKey(HKEY key, const TCHAR* keyName)
+{
+ return openKey(key, keyName, true);
+}
+
+HKEY
+ArchMiscWindows::addKey(HKEY key, const TCHAR* const* keyNames)
+{
+ return openKey(key, keyNames, true);
+}
+
+HKEY
+ArchMiscWindows::openKey(HKEY key, const TCHAR* keyName, bool create)
+{
+ // ignore if parent is NULL
+ if (key == NULL) {
+ return NULL;
+ }
+
+ // open next key
+ HKEY newKey;
+ LONG result = RegOpenKeyEx(key, keyName, 0,
+ KEY_WRITE | KEY_QUERY_VALUE, &newKey);
+ if (result != ERROR_SUCCESS && create) {
+ DWORD disp;
+ result = RegCreateKeyEx(key, keyName, 0, TEXT(""),
+ 0, KEY_WRITE | KEY_QUERY_VALUE,
+ NULL, &newKey, &disp);
+ }
+ if (result != ERROR_SUCCESS) {
+ RegCloseKey(key);
+ return NULL;
+ }
+
+ // switch to new key
+ RegCloseKey(key);
+ return newKey;
+}
+
+HKEY
+ArchMiscWindows::openKey(HKEY key, const TCHAR* const* keyNames, bool create)
+{
+ for (size_t i = 0; key != NULL && keyNames[i] != NULL; ++i) {
+ // open next key
+ key = openKey(key, keyNames[i], create);
+ }
+ return key;
+}
+
+void
+ArchMiscWindows::closeKey(HKEY key)
+{
+ assert(key != NULL);
+ if (key==NULL) return;
+ RegCloseKey(key);
+}
+
+void
+ArchMiscWindows::deleteKey(HKEY key, const TCHAR* name)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if (key==NULL || name==NULL) return;
+ RegDeleteKey(key, name);
+}
+
+void
+ArchMiscWindows::deleteValue(HKEY key, const TCHAR* name)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if (key==NULL || name==NULL) return;
+ RegDeleteValue(key, name);
+}
+
+bool
+ArchMiscWindows::hasValue(HKEY key, const TCHAR* name)
+{
+ DWORD type;
+ LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL);
+ return (result == ERROR_SUCCESS &&
+ (type == REG_DWORD || type == REG_SZ));
+}
+
+ArchMiscWindows::EValueType
+ArchMiscWindows::typeOfValue(HKEY key, const TCHAR* name)
+{
+ DWORD type;
+ LONG result = RegQueryValueEx(key, name, 0, &type, NULL, NULL);
+ if (result != ERROR_SUCCESS) {
+ return kNO_VALUE;
+ }
+ switch (type) {
+ case REG_DWORD:
+ return kUINT;
+
+ case REG_SZ:
+ return kSTRING;
+
+ case REG_BINARY:
+ return kBINARY;
+
+ default:
+ return kUNKNOWN;
+ }
+}
+
+void
+ArchMiscWindows::setValue(HKEY key,
+ const TCHAR* name, const std::string& value)
+{
+ assert(key != NULL);
+ if (key == NULL) {
+ // TODO: throw exception
+ return;
+ }
+ RegSetValueEx(key, name, 0, REG_SZ,
+ reinterpret_cast<const BYTE*>(value.c_str()),
+ (DWORD)value.size() + 1);
+}
+
+void
+ArchMiscWindows::setValue(HKEY key, const TCHAR* name, DWORD value)
+{
+ assert(key != NULL);
+ if (key == NULL) {
+ // TODO: throw exception
+ return;
+ }
+ RegSetValueEx(key, name, 0, REG_DWORD,
+ reinterpret_cast<CONST BYTE*>(&value),
+ sizeof(DWORD));
+}
+
+void
+ArchMiscWindows::setValueBinary(HKEY key,
+ const TCHAR* name, const std::string& value)
+{
+ assert(key != NULL);
+ assert(name != NULL);
+ if (key == NULL || name == NULL) {
+ // TODO: throw exception
+ return;
+ }
+ RegSetValueEx(key, name, 0, REG_BINARY,
+ reinterpret_cast<const BYTE*>(value.data()),
+ (DWORD)value.size());
+}
+
+std::string
+ArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR* name, DWORD type)
+{
+ // get the size of the string
+ DWORD actualType;
+ DWORD size = 0;
+ LONG result = RegQueryValueEx(key, name, 0, &actualType, NULL, &size);
+ if (result != ERROR_SUCCESS || actualType != type) {
+ return std::string();
+ }
+
+ // if zero size then return empty string
+ if (size == 0) {
+ return std::string();
+ }
+
+ // allocate space
+ char* buffer = new char[size];
+
+ // read it
+ result = RegQueryValueEx(key, name, 0, &actualType,
+ reinterpret_cast<BYTE*>(buffer), &size);
+ if (result != ERROR_SUCCESS || actualType != type) {
+ delete[] buffer;
+ return std::string();
+ }
+
+ // clean up and return value
+ if (type == REG_SZ && buffer[size - 1] == '\0') {
+ // don't include terminating nul; std::string will add one.
+ --size;
+ }
+ std::string value(buffer, size);
+ delete[] buffer;
+ return value;
+}
+
+std::string
+ArchMiscWindows::readValueString(HKEY key, const TCHAR* name)
+{
+ return readBinaryOrString(key, name, REG_SZ);
+}
+
+std::string
+ArchMiscWindows::readValueBinary(HKEY key, const TCHAR* name)
+{
+ return readBinaryOrString(key, name, REG_BINARY);
+}
+
+DWORD
+ArchMiscWindows::readValueInt(HKEY key, const TCHAR* name)
+{
+ DWORD type;
+ DWORD value;
+ DWORD size = sizeof(value);
+ LONG result = RegQueryValueEx(key, name, 0, &type,
+ reinterpret_cast<BYTE*>(&value), &size);
+ if (result != ERROR_SUCCESS || type != REG_DWORD) {
+ return 0;
+ }
+ return value;
+}
+
+void
+ArchMiscWindows::addDialog(HWND hwnd)
+{
+ s_dialogs->insert(hwnd);
+}
+
+void
+ArchMiscWindows::removeDialog(HWND hwnd)
+{
+ s_dialogs->erase(hwnd);
+}
+
+bool
+ArchMiscWindows::processDialog(MSG* msg)
+{
+ for (Dialogs::const_iterator index = s_dialogs->begin();
+ index != s_dialogs->end(); ++index) {
+ if (IsDialogMessage(*index, msg)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+ArchMiscWindows::addBusyState(DWORD busyModes)
+{
+ s_busyState |= busyModes;
+ setThreadExecutionState(s_busyState);
+}
+
+void
+ArchMiscWindows::removeBusyState(DWORD busyModes)
+{
+ s_busyState &= ~busyModes;
+ setThreadExecutionState(s_busyState);
+}
+
+void
+ArchMiscWindows::setThreadExecutionState(DWORD busyModes)
+{
+ // look up function dynamically so we work on older systems
+ if (s_stes == NULL) {
+ HINSTANCE kernel = LoadLibrary("kernel32.dll");
+ if (kernel != NULL) {
+ s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel,
+ "SetThreadExecutionState"));
+ }
+ if (s_stes == NULL) {
+ s_stes = &ArchMiscWindows::dummySetThreadExecutionState;
+ }
+ }
+
+ // convert to STES form
+ EXECUTION_STATE state = 0;
+ if ((busyModes & kSYSTEM) != 0) {
+ state |= ES_SYSTEM_REQUIRED;
+ }
+ if ((busyModes & kDISPLAY) != 0) {
+ state |= ES_DISPLAY_REQUIRED;
+ }
+ if (state != 0) {
+ state |= ES_CONTINUOUS;
+ }
+
+ // do it
+ s_stes(state);
+}
+
+DWORD
+ArchMiscWindows::dummySetThreadExecutionState(DWORD)
+{
+ // do nothing
+ return 0;
+}
+
+void
+ArchMiscWindows::wakeupDisplay()
+{
+ // We can't use ::setThreadExecutionState here because it sets
+ // ES_CONTINUOUS, which we don't want.
+
+ if (s_stes == NULL) {
+ HINSTANCE kernel = LoadLibrary("kernel32.dll");
+ if (kernel != NULL) {
+ s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel,
+ "SetThreadExecutionState"));
+ }
+ if (s_stes == NULL) {
+ s_stes = &ArchMiscWindows::dummySetThreadExecutionState;
+ }
+ }
+
+ s_stes(ES_DISPLAY_REQUIRED);
+
+ // restore the original execution states
+ setThreadExecutionState(s_busyState);
+}
+
+bool
+ArchMiscWindows::wasLaunchedAsService()
+{
+ String name;
+ if (!getParentProcessName(name)) {
+ LOG((CLOG_ERR "cannot determine if process was launched as service"));
+ return false;
+ }
+
+ return (name == SERVICE_LAUNCHER);
+}
+
+bool
+ArchMiscWindows::getParentProcessName(String &name)
+{
+ PROCESSENTRY32 parentEntry;
+ if (!getParentProcessEntry(parentEntry)){
+ LOG((CLOG_ERR "could not get entry for parent process"));
+ return false;
+ }
+
+ name = parentEntry.szExeFile;
+ return true;
+}
+
+BOOL WINAPI
+ArchMiscWindows::getSelfProcessEntry(PROCESSENTRY32& entry)
+{
+ // get entry from current PID
+ return getProcessEntry(entry, GetCurrentProcessId());
+}
+
+BOOL WINAPI
+ArchMiscWindows::getParentProcessEntry(PROCESSENTRY32& entry)
+{
+ // get the current process, so we can get parent PID
+ PROCESSENTRY32 selfEntry;
+ if (!getSelfProcessEntry(selfEntry)) {
+ return FALSE;
+ }
+
+ // get entry from parent PID
+ return getProcessEntry(entry, selfEntry.th32ParentProcessID);
+}
+
+BOOL WINAPI
+ArchMiscWindows::getProcessEntry(PROCESSENTRY32& entry, DWORD processID)
+{
+ // first we need to take a snapshot of the running processes
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) {
+ LOG((CLOG_ERR "could not get process snapshot (error: %i)",
+ GetLastError()));
+ return FALSE;
+ }
+
+ entry.dwSize = sizeof(PROCESSENTRY32);
+
+ // get the first process, and if we can't do that then it's
+ // unlikely we can go any further
+ BOOL gotEntry = Process32First(snapshot, &entry);
+ if (!gotEntry) {
+ LOG((CLOG_ERR "could not get first process entry (error: %i)",
+ GetLastError()));
+ return FALSE;
+ }
+
+ while(gotEntry) {
+
+ if (entry.th32ProcessID == processID) {
+ // found current process
+ return TRUE;
+ }
+
+ // now move on to the next entry (when we reach end, loop will stop)
+ gotEntry = Process32Next(snapshot, &entry);
+ }
+
+ return FALSE;
+}
+
+HINSTANCE
+ArchMiscWindows::instanceWin32()
+{
+ assert(s_instanceWin32 != NULL);
+ return s_instanceWin32;
+}
+
+void
+ArchMiscWindows::setInstanceWin32(HINSTANCE instance)
+{
+ assert(instance != NULL);
+ s_instanceWin32 = instance;
+} \ No newline at end of file
diff --git a/src/lib/arch/win32/ArchMiscWindows.h b/src/lib/arch/win32/ArchMiscWindows.h
new file mode 100644
index 0000000..0ecd79d
--- /dev/null
+++ b/src/lib/arch/win32/ArchMiscWindows.h
@@ -0,0 +1,202 @@
+/*
+ * barrier -- 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 "common/common.h"
+#include "common/stdstring.h"
+#include "common/stdset.h"
+#include "base/String.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <Tlhelp32.h>
+
+//! Miscellaneous win32 functions.
+class ArchMiscWindows {
+public:
+ enum EValueType {
+ kUNKNOWN,
+ kNO_VALUE,
+ kUINT,
+ kSTRING,
+ kBINARY
+ };
+ enum EBusyModes {
+ kIDLE = 0x0000,
+ kSYSTEM = 0x0001,
+ kDISPLAY = 0x0002
+ };
+
+ typedef int (*RunFunc)(void);
+
+ //! Initialize
+ static void init();
+
+ //! Delete memory
+ static void cleanup();
+
+ //! Set the application icons
+ /*!
+ Set the application icons.
+ */
+ static void setIcons(HICON largeIcon, HICON smallIcon);
+
+ //! Get the application icons
+ /*!
+ Get the application icons.
+ */
+ static void getIcons(HICON& largeIcon, HICON& smallIcon);
+
+ //! Run the daemon
+ /*!
+ Delegates to ArchDaemonWindows.
+ */
+ static int runDaemon(RunFunc runFunc);
+
+ //! Indicate daemon is in main loop
+ /*!
+ Delegates to ArchDaemonWindows.
+ */
+ static void daemonRunning(bool running);
+
+ //! Indicate failure of running daemon
+ /*!
+ Delegates to ArchDaemonWindows.
+ */
+ static void daemonFailed(int result);
+
+ //! Get daemon quit message
+ /*!
+ Delegates to ArchDaemonWindows.
+ */
+ static UINT getDaemonQuitMessage();
+
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* child);
+
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* const* keyPath);
+
+ //! Open/create and return a registry key, closing the parent key
+ static HKEY addKey(HKEY parent, const TCHAR* child);
+
+ //! Open/create and return a registry key, closing the parent key
+ static HKEY addKey(HKEY parent, const TCHAR* const* keyPath);
+
+ //! Close a key
+ static void closeKey(HKEY);
+
+ //! Delete a key (which should have no subkeys)
+ static void deleteKey(HKEY parent, const TCHAR* name);
+
+ //! Delete a value
+ static void deleteValue(HKEY parent, const TCHAR* name);
+
+ //! Test if a value exists
+ static bool hasValue(HKEY key, const TCHAR* name);
+
+ //! Get type of value
+ static EValueType typeOfValue(HKEY key, const TCHAR* name);
+
+ //! Set a string value in the registry
+ static void setValue(HKEY key, const TCHAR* name,
+ const std::string& value);
+
+ //! Set a DWORD value in the registry
+ static void setValue(HKEY key, const TCHAR* name, DWORD value);
+
+ //! Set a BINARY value in the registry
+ /*!
+ Sets the \p name value of \p key to \p value.data().
+ */
+ static void setValueBinary(HKEY key, const TCHAR* name,
+ const std::string& value);
+
+ //! Read a string value from the registry
+ static std::string readValueString(HKEY, const TCHAR* name);
+
+ //! Read a DWORD value from the registry
+ static DWORD readValueInt(HKEY, const TCHAR* name);
+
+ //! Read a BINARY value from the registry
+ static std::string readValueBinary(HKEY, const TCHAR* name);
+
+ //! Add a dialog
+ static void addDialog(HWND);
+
+ //! Remove a dialog
+ static void removeDialog(HWND);
+
+ //! Process dialog message
+ /*!
+ Checks if the message is destined for a dialog. If so the message
+ is passed to the dialog and returns true, otherwise returns false.
+ */
+ static bool processDialog(MSG*);
+
+ //! Disable power saving
+ static void addBusyState(DWORD busyModes);
+
+ //! Enable power saving
+ static void removeBusyState(DWORD busyModes);
+
+ //! Briefly interrupt power saving
+ static void wakeupDisplay();
+
+ //! Returns true if this process was launched via NT service host.
+ static bool wasLaunchedAsService();
+
+ //! Returns true if we got the parent process name.
+ static bool getParentProcessName(String &name);
+
+ static HINSTANCE instanceWin32();
+
+ static void setInstanceWin32(HINSTANCE instance);
+
+ static BOOL WINAPI getProcessEntry(PROCESSENTRY32& entry, DWORD processID);
+ static BOOL WINAPI getSelfProcessEntry(PROCESSENTRY32& entry);
+ static BOOL WINAPI getParentProcessEntry(PROCESSENTRY32& entry);
+
+private:
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* child, bool create);
+
+ //! Open and return a registry key, closing the parent key
+ static HKEY openKey(HKEY parent, const TCHAR* const* keyPath,
+ bool create);
+
+ //! Read a string value from the registry
+ static std::string readBinaryOrString(HKEY, const TCHAR* name, DWORD type);
+
+ //! Set thread busy state
+ static void setThreadExecutionState(DWORD);
+
+ static DWORD WINAPI dummySetThreadExecutionState(DWORD);
+
+private:
+ typedef std::set<HWND> Dialogs;
+ typedef DWORD (WINAPI *STES_t)(DWORD);
+
+ static Dialogs* s_dialogs;
+ static DWORD s_busyState;
+ static STES_t s_stes;
+ static HICON s_largeIcon;
+ static HICON s_smallIcon;
+ static HINSTANCE s_instanceWin32;
+};
diff --git a/src/lib/arch/win32/ArchMultithreadWindows.cpp b/src/lib/arch/win32/ArchMultithreadWindows.cpp
new file mode 100644
index 0000000..d3fd059
--- /dev/null
+++ b/src/lib/arch/win32/ArchMultithreadWindows.cpp
@@ -0,0 +1,705 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#if defined(_MSC_VER) && !defined(_MT)
+# error multithreading compile option is required
+#endif
+
+#include "arch/win32/ArchMultithreadWindows.h"
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+
+#include <process.h>
+
+//
+// note -- implementation of condition variable taken from:
+// http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
+// titled "Strategies for Implementing POSIX Condition Variables
+// on Win32." it also provides an implementation that doesn't
+// suffer from the incorrectness problem described in our
+// corresponding header but it is slower, still unfair, and
+// can cause busy waiting.
+//
+
+//
+// ArchThreadImpl
+//
+
+class ArchThreadImpl {
+public:
+ ArchThreadImpl();
+ ~ArchThreadImpl();
+
+public:
+ int m_refCount;
+ HANDLE m_thread;
+ DWORD m_id;
+ IArchMultithread::ThreadFunc m_func;
+ void* m_userData;
+ HANDLE m_cancel;
+ bool m_cancelling;
+ HANDLE m_exit;
+ void* m_result;
+ void* m_networkData;
+};
+
+ArchThreadImpl::ArchThreadImpl() :
+ m_refCount(1),
+ m_thread(NULL),
+ m_id(0),
+ m_func(NULL),
+ m_userData(NULL),
+ m_cancelling(false),
+ m_result(NULL),
+ m_networkData(NULL)
+{
+ m_exit = CreateEvent(NULL, TRUE, FALSE, NULL);
+ m_cancel = CreateEvent(NULL, TRUE, FALSE, NULL);
+}
+
+ArchThreadImpl::~ArchThreadImpl()
+{
+ CloseHandle(m_exit);
+ CloseHandle(m_cancel);
+}
+
+
+//
+// ArchMultithreadWindows
+//
+
+ArchMultithreadWindows* ArchMultithreadWindows::s_instance = NULL;
+
+ArchMultithreadWindows::ArchMultithreadWindows()
+{
+ assert(s_instance == NULL);
+ s_instance = this;
+
+ // no signal handlers
+ for (size_t i = 0; i < kNUM_SIGNALS; ++i) {
+ m_signalFunc[i] = NULL;
+ m_signalUserData[i] = NULL;
+ }
+
+ // create mutex for thread list
+ m_threadMutex = newMutex();
+
+ // create thread for calling (main) thread and add it to our
+ // list. no need to lock the mutex since we're the only thread.
+ m_mainThread = new ArchThreadImpl;
+ m_mainThread->m_thread = NULL;
+ m_mainThread->m_id = GetCurrentThreadId();
+ insert(m_mainThread);
+}
+
+ArchMultithreadWindows::~ArchMultithreadWindows()
+{
+ s_instance = NULL;
+
+ // clean up thread list
+ for (ThreadList::iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ delete *index;
+ }
+
+ // done with mutex
+ delete m_threadMutex;
+}
+
+void
+ArchMultithreadWindows::setNetworkDataForCurrentThread(void* data)
+{
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
+ thread->m_networkData = data;
+ unlockMutex(m_threadMutex);
+}
+
+void*
+ArchMultithreadWindows::getNetworkDataForThread(ArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* data = thread->m_networkData;
+ unlockMutex(m_threadMutex);
+ return data;
+}
+
+HANDLE
+ArchMultithreadWindows::getCancelEventForCurrentThread()
+{
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
+ unlockMutex(m_threadMutex);
+ return thread->m_cancel;
+}
+
+ArchMultithreadWindows*
+ArchMultithreadWindows::getInstance()
+{
+ return s_instance;
+}
+
+ArchCond
+ArchMultithreadWindows::newCondVar()
+{
+ ArchCondImpl* cond = new ArchCondImpl;
+ cond->m_events[ArchCondImpl::kSignal] = CreateEvent(NULL,
+ FALSE, FALSE, NULL);
+ cond->m_events[ArchCondImpl::kBroadcast] = CreateEvent(NULL,
+ TRUE, FALSE, NULL);
+ cond->m_waitCountMutex = newMutex();
+ cond->m_waitCount = 0;
+ return cond;
+}
+
+void
+ArchMultithreadWindows::closeCondVar(ArchCond cond)
+{
+ CloseHandle(cond->m_events[ArchCondImpl::kSignal]);
+ CloseHandle(cond->m_events[ArchCondImpl::kBroadcast]);
+ closeMutex(cond->m_waitCountMutex);
+ delete cond;
+}
+
+void
+ArchMultithreadWindows::signalCondVar(ArchCond cond)
+{
+ // is anybody waiting?
+ lockMutex(cond->m_waitCountMutex);
+ const bool hasWaiter = (cond->m_waitCount > 0);
+ unlockMutex(cond->m_waitCountMutex);
+
+ // wake one thread if anybody is waiting
+ if (hasWaiter) {
+ SetEvent(cond->m_events[ArchCondImpl::kSignal]);
+ }
+}
+
+void
+ArchMultithreadWindows::broadcastCondVar(ArchCond cond)
+{
+ // is anybody waiting?
+ lockMutex(cond->m_waitCountMutex);
+ const bool hasWaiter = (cond->m_waitCount > 0);
+ unlockMutex(cond->m_waitCountMutex);
+
+ // wake all threads if anybody is waiting
+ if (hasWaiter) {
+ SetEvent(cond->m_events[ArchCondImpl::kBroadcast]);
+ }
+}
+
+bool
+ArchMultithreadWindows::waitCondVar(ArchCond cond,
+ ArchMutex mutex, double timeout)
+{
+ // prepare to wait
+ const DWORD winTimeout = (timeout < 0.0) ? INFINITE :
+ static_cast<DWORD>(1000.0 * timeout);
+
+ // make a list of the condition variable events and the cancel event
+ // for the current thread.
+ HANDLE handles[4];
+ handles[0] = cond->m_events[ArchCondImpl::kSignal];
+ handles[1] = cond->m_events[ArchCondImpl::kBroadcast];
+ handles[2] = getCancelEventForCurrentThread();
+
+ // update waiter count
+ lockMutex(cond->m_waitCountMutex);
+ ++cond->m_waitCount;
+ unlockMutex(cond->m_waitCountMutex);
+
+ // release mutex. this should be atomic with the wait so that it's
+ // impossible for another thread to signal us between the unlock and
+ // the wait, which would lead to a lost signal on broadcasts.
+ // however, we're using a manual reset event for broadcasts which
+ // stays set until we reset it, so we don't lose the broadcast.
+ unlockMutex(mutex);
+
+ // wait for a signal or broadcast
+ // TODO: this doesn't always signal when kill signals are sent
+ DWORD result = WaitForMultipleObjects(3, handles, FALSE, winTimeout);
+
+ // cancel takes priority
+ if (result != WAIT_OBJECT_0 + 2 &&
+ WaitForSingleObject(handles[2], 0) == WAIT_OBJECT_0) {
+ result = WAIT_OBJECT_0 + 2;
+ }
+
+ // update the waiter count and check if we're the last waiter
+ lockMutex(cond->m_waitCountMutex);
+ --cond->m_waitCount;
+ const bool last = (result == WAIT_OBJECT_0 + 1 && cond->m_waitCount == 0);
+ unlockMutex(cond->m_waitCountMutex);
+
+ // reset the broadcast event if we're the last waiter
+ if (last) {
+ ResetEvent(cond->m_events[ArchCondImpl::kBroadcast]);
+ }
+
+ // reacquire the mutex
+ lockMutex(mutex);
+
+ // cancel thread if necessary
+ if (result == WAIT_OBJECT_0 + 2) {
+ ARCH->testCancelThread();
+ }
+
+ // return success or failure
+ return (result == WAIT_OBJECT_0 + 0 ||
+ result == WAIT_OBJECT_0 + 1);
+}
+
+ArchMutex
+ArchMultithreadWindows::newMutex()
+{
+ ArchMutexImpl* mutex = new ArchMutexImpl;
+ InitializeCriticalSection(&mutex->m_mutex);
+ return mutex;
+}
+
+void
+ArchMultithreadWindows::closeMutex(ArchMutex mutex)
+{
+ DeleteCriticalSection(&mutex->m_mutex);
+ delete mutex;
+}
+
+void
+ArchMultithreadWindows::lockMutex(ArchMutex mutex)
+{
+ EnterCriticalSection(&mutex->m_mutex);
+}
+
+void
+ArchMultithreadWindows::unlockMutex(ArchMutex mutex)
+{
+ LeaveCriticalSection(&mutex->m_mutex);
+}
+
+ArchThread
+ArchMultithreadWindows::newThread(ThreadFunc func, void* data)
+{
+ lockMutex(m_threadMutex);
+
+ // create thread impl for new thread
+ ArchThreadImpl* thread = new ArchThreadImpl;
+ thread->m_func = func;
+ thread->m_userData = data;
+
+ // create thread
+ unsigned int id = 0;
+ thread->m_thread = reinterpret_cast<HANDLE>(_beginthreadex(NULL, 0,
+ threadFunc, (void*)thread, 0, &id));
+ thread->m_id = static_cast<DWORD>(id);
+
+ // check if thread was started
+ if (thread->m_thread == 0) {
+ // failed to start thread so clean up
+ delete thread;
+ thread = NULL;
+ }
+ else {
+ // add thread to list
+ insert(thread);
+
+ // increment ref count to account for the thread itself
+ refThread(thread);
+ }
+
+ // note that the child thread will wait until we release this mutex
+ unlockMutex(m_threadMutex);
+
+ return thread;
+}
+
+ArchThread
+ArchMultithreadWindows::newCurrentThread()
+{
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = find(GetCurrentThreadId());
+ unlockMutex(m_threadMutex);
+ assert(thread != NULL);
+ return thread;
+}
+
+void
+ArchMultithreadWindows::closeThread(ArchThread thread)
+{
+ assert(thread != NULL);
+
+ // decrement ref count and clean up thread if no more references
+ if (--thread->m_refCount == 0) {
+ // close the handle (main thread has a NULL handle)
+ if (thread->m_thread != NULL) {
+ CloseHandle(thread->m_thread);
+ }
+
+ // remove thread from list
+ lockMutex(m_threadMutex);
+ assert(findNoRefOrCreate(thread->m_id) == thread);
+ erase(thread);
+ unlockMutex(m_threadMutex);
+
+ // done with thread
+ delete thread;
+ }
+}
+
+ArchThread
+ArchMultithreadWindows::copyThread(ArchThread thread)
+{
+ refThread(thread);
+ return thread;
+}
+
+void
+ArchMultithreadWindows::cancelThread(ArchThread thread)
+{
+ assert(thread != NULL);
+
+ // set cancel flag
+ SetEvent(thread->m_cancel);
+}
+
+void
+ArchMultithreadWindows::setPriorityOfThread(ArchThread thread, int n)
+{
+ struct PriorityInfo {
+ public:
+ DWORD m_class;
+ int m_level;
+ };
+ static const PriorityInfo s_pClass[] = {
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_IDLE },
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
+ { IDLE_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
+ { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
+ { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
+ { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
+ { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
+ { NORMAL_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
+ { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
+ { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
+ { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
+ { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
+ { HIGH_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_IDLE },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_LOWEST },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_BELOW_NORMAL },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_NORMAL },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_ABOVE_NORMAL },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_HIGHEST },
+ { REALTIME_PRIORITY_CLASS, THREAD_PRIORITY_TIME_CRITICAL}
+ };
+#if defined(_DEBUG)
+ // don't use really high priorities when debugging
+ static const size_t s_pMax = 13;
+#else
+ static const size_t s_pMax = sizeof(s_pClass) / sizeof(s_pClass[0]) - 1;
+#endif
+ static const size_t s_pBase = 8; // index of normal priority
+
+ assert(thread != NULL);
+
+ size_t index;
+ if (n > 0 && s_pBase < (size_t)n) {
+ // lowest priority
+ index = 0;
+ }
+ else {
+ index = (size_t)((int)s_pBase - n);
+ if (index > s_pMax) {
+ // highest priority
+ index = s_pMax;
+ }
+ }
+ SetPriorityClass(GetCurrentProcess(), s_pClass[index].m_class);
+ SetThreadPriority(thread->m_thread, s_pClass[index].m_level);
+}
+
+void
+ArchMultithreadWindows::testCancelThread()
+{
+ // find current thread
+ lockMutex(m_threadMutex);
+ ArchThreadImpl* thread = findNoRef(GetCurrentThreadId());
+ unlockMutex(m_threadMutex);
+
+ // test cancel on thread
+ testCancelThreadImpl(thread);
+}
+
+bool
+ArchMultithreadWindows::wait(ArchThread target, double timeout)
+{
+ assert(target != NULL);
+
+ lockMutex(m_threadMutex);
+
+ // find current thread
+ ArchThreadImpl* self = findNoRef(GetCurrentThreadId());
+
+ // ignore wait if trying to wait on ourself
+ if (target == self) {
+ unlockMutex(m_threadMutex);
+ return false;
+ }
+
+ // ref the target so it can't go away while we're watching it
+ refThread(target);
+
+ unlockMutex(m_threadMutex);
+
+ // convert timeout
+ DWORD t;
+ if (timeout < 0.0) {
+ t = INFINITE;
+ }
+ else {
+ t = (DWORD)(1000.0 * timeout);
+ }
+
+ // wait for this thread to be cancelled or woken up or for the
+ // target thread to terminate.
+ HANDLE handles[2];
+ handles[0] = target->m_exit;
+ handles[1] = self->m_cancel;
+ DWORD result = WaitForMultipleObjects(2, handles, FALSE, t);
+
+ // cancel takes priority
+ if (result != WAIT_OBJECT_0 + 1 &&
+ WaitForSingleObject(handles[1], 0) == WAIT_OBJECT_0) {
+ result = WAIT_OBJECT_0 + 1;
+ }
+
+ // release target
+ closeThread(target);
+
+ // handle result
+ switch (result) {
+ case WAIT_OBJECT_0 + 0:
+ // target thread terminated
+ return true;
+
+ case WAIT_OBJECT_0 + 1:
+ // this thread was cancelled. does not return.
+ testCancelThreadImpl(self);
+
+ default:
+ // timeout or error
+ return false;
+ }
+}
+
+bool
+ArchMultithreadWindows::isSameThread(ArchThread thread1, ArchThread thread2)
+{
+ return (thread1 == thread2);
+}
+
+bool
+ArchMultithreadWindows::isExitedThread(ArchThread thread)
+{
+ // poll exit event
+ return (WaitForSingleObject(thread->m_exit, 0) == WAIT_OBJECT_0);
+}
+
+void*
+ArchMultithreadWindows::getResultOfThread(ArchThread thread)
+{
+ lockMutex(m_threadMutex);
+ void* result = thread->m_result;
+ unlockMutex(m_threadMutex);
+ return result;
+}
+
+IArchMultithread::ThreadID
+ArchMultithreadWindows::getIDOfThread(ArchThread thread)
+{
+ return static_cast<ThreadID>(thread->m_id);
+}
+
+void
+ArchMultithreadWindows::setSignalHandler(
+ ESignal signal, SignalFunc func, void* userData)
+{
+ lockMutex(m_threadMutex);
+ m_signalFunc[signal] = func;
+ m_signalUserData[signal] = userData;
+ unlockMutex(m_threadMutex);
+}
+
+void
+ArchMultithreadWindows::raiseSignal(ESignal signal)
+{
+ lockMutex(m_threadMutex);
+ if (m_signalFunc[signal] != NULL) {
+ m_signalFunc[signal](signal, m_signalUserData[signal]);
+ ARCH->unblockPollSocket(m_mainThread);
+ }
+ else if (signal == kINTERRUPT || signal == kTERMINATE) {
+ ARCH->cancelThread(m_mainThread);
+ }
+ unlockMutex(m_threadMutex);
+}
+
+ArchThreadImpl*
+ArchMultithreadWindows::find(DWORD id)
+{
+ ArchThreadImpl* impl = findNoRef(id);
+ if (impl != NULL) {
+ refThread(impl);
+ }
+ return impl;
+}
+
+ArchThreadImpl*
+ArchMultithreadWindows::findNoRef(DWORD id)
+{
+ ArchThreadImpl* impl = findNoRefOrCreate(id);
+ if (impl == NULL) {
+ // create thread for calling thread which isn't in our list and
+ // add it to the list. this won't normally happen but it can if
+ // the system calls us under a new thread, like it does when we
+ // run as a service.
+ impl = new ArchThreadImpl;
+ impl->m_thread = NULL;
+ impl->m_id = GetCurrentThreadId();
+ insert(impl);
+ }
+ return impl;
+}
+
+ArchThreadImpl*
+ArchMultithreadWindows::findNoRefOrCreate(DWORD id)
+{
+ // linear search
+ for (ThreadList::const_iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if ((*index)->m_id == id) {
+ return *index;
+ }
+ }
+ return NULL;
+}
+
+void
+ArchMultithreadWindows::insert(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+
+ // thread shouldn't already be on the list
+ assert(findNoRefOrCreate(thread->m_id) == NULL);
+
+ // append to list
+ m_threadList.push_back(thread);
+}
+
+void
+ArchMultithreadWindows::erase(ArchThreadImpl* thread)
+{
+ for (ThreadList::iterator index = m_threadList.begin();
+ index != m_threadList.end(); ++index) {
+ if (*index == thread) {
+ m_threadList.erase(index);
+ break;
+ }
+ }
+}
+
+void
+ArchMultithreadWindows::refThread(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+ assert(findNoRefOrCreate(thread->m_id) != NULL);
+ ++thread->m_refCount;
+}
+
+void
+ArchMultithreadWindows::testCancelThreadImpl(ArchThreadImpl* thread)
+{
+ assert(thread != NULL);
+
+ // poll cancel event. return if not set.
+ const DWORD result = WaitForSingleObject(thread->m_cancel, 0);
+ if (result != WAIT_OBJECT_0) {
+ return;
+ }
+
+ // update cancel state
+ lockMutex(m_threadMutex);
+ bool cancel = !thread->m_cancelling;
+ thread->m_cancelling = true;
+ ResetEvent(thread->m_cancel);
+ unlockMutex(m_threadMutex);
+
+ // unwind thread's stack if cancelling
+ if (cancel) {
+ throw XThreadCancel();
+ }
+}
+
+unsigned int __stdcall
+ArchMultithreadWindows::threadFunc(void* vrep)
+{
+ // get the thread
+ ArchThreadImpl* thread = static_cast<ArchThreadImpl*>(vrep);
+
+ // run thread
+ s_instance->doThreadFunc(thread);
+
+ // terminate the thread
+ return 0;
+}
+
+void
+ArchMultithreadWindows::doThreadFunc(ArchThread thread)
+{
+ // wait for parent to initialize this object
+ lockMutex(m_threadMutex);
+ unlockMutex(m_threadMutex);
+
+ void* result = NULL;
+ try {
+ // go
+ result = (*thread->m_func)(thread->m_userData);
+ }
+
+ catch (XThreadCancel&) {
+ // client called cancel()
+ }
+ catch (...) {
+ // note -- don't catch (...) to avoid masking bugs
+ SetEvent(thread->m_exit);
+ closeThread(thread);
+ throw;
+ }
+
+ // thread has exited
+ lockMutex(m_threadMutex);
+ thread->m_result = result;
+ unlockMutex(m_threadMutex);
+ SetEvent(thread->m_exit);
+
+ // done with thread
+ closeThread(thread);
+}
diff --git a/src/lib/arch/win32/ArchMultithreadWindows.h b/src/lib/arch/win32/ArchMultithreadWindows.h
new file mode 100644
index 0000000..99aa640
--- /dev/null
+++ b/src/lib/arch/win32/ArchMultithreadWindows.h
@@ -0,0 +1,116 @@
+/*
+ * barrier -- 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 "arch/IArchMultithread.h"
+#include "common/stdlist.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#define ARCH_MULTITHREAD ArchMultithreadWindows
+
+class ArchCondImpl {
+public:
+ enum { kSignal = 0, kBroadcast };
+
+ HANDLE m_events[2];
+ mutable int m_waitCount;
+ ArchMutex m_waitCountMutex;
+};
+
+class ArchMutexImpl {
+public:
+ CRITICAL_SECTION m_mutex;
+};
+
+//! Win32 implementation of IArchMultithread
+class ArchMultithreadWindows : public IArchMultithread {
+public:
+ ArchMultithreadWindows();
+ virtual ~ArchMultithreadWindows();
+
+ //! @name manipulators
+ //@{
+
+ void setNetworkDataForCurrentThread(void*);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ HANDLE getCancelEventForCurrentThread();
+
+ void* getNetworkDataForThread(ArchThread);
+
+ static ArchMultithreadWindows* getInstance();
+
+ //@}
+
+ // IArchMultithread overrides
+ virtual ArchCond newCondVar();
+ virtual void closeCondVar(ArchCond);
+ virtual void signalCondVar(ArchCond);
+ virtual void broadcastCondVar(ArchCond);
+ virtual bool waitCondVar(ArchCond, ArchMutex, double timeout);
+ virtual ArchMutex newMutex();
+ virtual void closeMutex(ArchMutex);
+ virtual void lockMutex(ArchMutex);
+ virtual void unlockMutex(ArchMutex);
+ virtual ArchThread newThread(ThreadFunc, void*);
+ virtual ArchThread newCurrentThread();
+ virtual ArchThread copyThread(ArchThread);
+ virtual void closeThread(ArchThread);
+ virtual void cancelThread(ArchThread);
+ virtual void setPriorityOfThread(ArchThread, int n);
+ virtual void testCancelThread();
+ virtual bool wait(ArchThread, double timeout);
+ virtual bool isSameThread(ArchThread, ArchThread);
+ virtual bool isExitedThread(ArchThread);
+ virtual void* getResultOfThread(ArchThread);
+ virtual ThreadID getIDOfThread(ArchThread);
+ virtual void setSignalHandler(ESignal, SignalFunc, void*);
+ virtual void raiseSignal(ESignal);
+
+private:
+ ArchThreadImpl* find(DWORD id);
+ ArchThreadImpl* findNoRef(DWORD id);
+ ArchThreadImpl* findNoRefOrCreate(DWORD id);
+ void insert(ArchThreadImpl* thread);
+ void erase(ArchThreadImpl* thread);
+
+ void refThread(ArchThreadImpl* rep);
+ void testCancelThreadImpl(ArchThreadImpl* rep);
+
+ void doThreadFunc(ArchThread thread);
+ static unsigned int __stdcall threadFunc(void* vrep);
+
+private:
+ typedef std::list<ArchThread> ThreadList;
+
+ static ArchMultithreadWindows* s_instance;
+
+ ArchMutex m_threadMutex;
+
+ ThreadList m_threadList;
+ ArchThread m_mainThread;
+
+ SignalFunc m_signalFunc[kNUM_SIGNALS];
+ void* m_signalUserData[kNUM_SIGNALS];
+};
diff --git a/src/lib/arch/win32/ArchNetworkWinsock.cpp b/src/lib/arch/win32/ArchNetworkWinsock.cpp
new file mode 100644
index 0000000..722c4c5
--- /dev/null
+++ b/src/lib/arch/win32/ArchNetworkWinsock.cpp
@@ -0,0 +1,985 @@
+/*
+ * barrier -- 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 "arch/win32/ArchNetworkWinsock.h"
+#include "arch/win32/ArchMultithreadWindows.h"
+#include "arch/win32/XArchWindows.h"
+#include "arch/IArchMultithread.h"
+#include "arch/Arch.h"
+
+#include <malloc.h>
+
+static const int s_family[] = {
+ PF_UNSPEC,
+ PF_INET,
+ PF_INET6,
+};
+static const int s_type[] = {
+ SOCK_DGRAM,
+ SOCK_STREAM
+};
+
+static SOCKET (PASCAL FAR *accept_winsock)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen);
+static int (PASCAL FAR *bind_winsock)(SOCKET s, const struct sockaddr FAR *addr, int namelen);
+static int (PASCAL FAR *close_winsock)(SOCKET s);
+static int (PASCAL FAR *connect_winsock)(SOCKET s, const struct sockaddr FAR *name, int namelen);
+static int (PASCAL FAR *gethostname_winsock)(char FAR * name, int namelen);
+static int (PASCAL FAR *getsockerror_winsock)(void);
+static int (PASCAL FAR *getsockopt_winsock)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen);
+static u_short (PASCAL FAR *htons_winsock)(u_short v);
+static char FAR * (PASCAL FAR *inet_ntoa_winsock)(struct in_addr in);
+static unsigned long (PASCAL FAR *inet_addr_winsock)(const char FAR * cp);
+static int (PASCAL FAR *ioctl_winsock)(SOCKET s, int cmd, void FAR * data);
+static int (PASCAL FAR *listen_winsock)(SOCKET s, int backlog);
+static u_short (PASCAL FAR *ntohs_winsock)(u_short v);
+static int (PASCAL FAR *recv_winsock)(SOCKET s, void FAR * buf, int len, int flags);
+static int (PASCAL FAR *select_winsock)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout);
+static int (PASCAL FAR *send_winsock)(SOCKET s, const void FAR * buf, int len, int flags);
+static int (PASCAL FAR *setsockopt_winsock)(SOCKET s, int level, int optname, const void FAR * optval, int optlen);
+static int (PASCAL FAR *shutdown_winsock)(SOCKET s, int how);
+static SOCKET (PASCAL FAR *socket_winsock)(int af, int type, int protocol);
+static struct hostent FAR * (PASCAL FAR *gethostbyaddr_winsock)(const char FAR * addr, int len, int type);
+static struct hostent FAR * (PASCAL FAR *gethostbyname_winsock)(const char FAR * name);
+static int (PASCAL FAR *WSACleanup_winsock)(void);
+static int (PASCAL FAR *WSAFDIsSet_winsock)(SOCKET, fd_set FAR * fdset);
+static WSAEVENT (PASCAL FAR *WSACreateEvent_winsock)(void);
+static BOOL (PASCAL FAR *WSACloseEvent_winsock)(WSAEVENT);
+static BOOL (PASCAL FAR *WSASetEvent_winsock)(WSAEVENT);
+static BOOL (PASCAL FAR *WSAResetEvent_winsock)(WSAEVENT);
+static int (PASCAL FAR *WSAEventSelect_winsock)(SOCKET, WSAEVENT, long);
+static DWORD (PASCAL FAR *WSAWaitForMultipleEvents_winsock)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL);
+static int (PASCAL FAR *WSAEnumNetworkEvents_winsock)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS);
+
+#undef FD_ISSET
+#define FD_ISSET(fd, set) WSAFDIsSet_winsock((SOCKET)(fd), (fd_set FAR *)(set))
+
+#define setfunc(var, name, type) var = (type)netGetProcAddress(module, #name)
+
+static HMODULE s_networkModule = NULL;
+
+static
+FARPROC
+netGetProcAddress(HMODULE module, LPCSTR name)
+{
+ FARPROC func = ::GetProcAddress(module, name);
+ if (!func) {
+ throw XArchNetworkSupport("");
+ }
+ return func;
+}
+
+ArchNetAddressImpl*
+ArchNetAddressImpl::alloc(size_t size)
+{
+ size_t totalSize = size + ADDR_HDR_SIZE;
+ ArchNetAddressImpl* addr = (ArchNetAddressImpl*)malloc(totalSize);
+ addr->m_len = (int)size;
+ return addr;
+}
+
+
+//
+// ArchNetworkWinsock
+//
+
+ArchNetworkWinsock::ArchNetworkWinsock() :
+ m_mutex(NULL)
+{
+}
+
+ArchNetworkWinsock::~ArchNetworkWinsock()
+{
+ if (s_networkModule != NULL) {
+ WSACleanup_winsock();
+ ::FreeLibrary(s_networkModule);
+
+ WSACleanup_winsock = NULL;
+ s_networkModule = NULL;
+ }
+ if (m_mutex != NULL) {
+ ARCH->closeMutex(m_mutex);
+ }
+
+ EventList::iterator it;
+ for (it = m_unblockEvents.begin(); it != m_unblockEvents.end(); it++) {
+ delete *it;
+ }
+}
+
+void
+ArchNetworkWinsock::init()
+{
+ static const char* s_library[] = { "ws2_32.dll" };
+
+ assert(WSACleanup_winsock == NULL);
+ assert(s_networkModule == NULL);
+
+ // try each winsock library
+ for (size_t i = 0; i < sizeof(s_library) / sizeof(s_library[0]); ++i) {
+ try {
+ initModule((HMODULE)::LoadLibrary(s_library[i]));
+ m_mutex = ARCH->newMutex();
+ return;
+ }
+ catch (XArchNetwork&) {
+ // ignore
+ }
+ }
+
+ // can't initialize any library
+ throw XArchNetworkSupport("Cannot load winsock library");
+}
+
+void
+ArchNetworkWinsock::initModule(HMODULE module)
+{
+ if (module == NULL) {
+ throw XArchNetworkSupport("");
+ }
+
+ // get startup function address
+ int (PASCAL FAR *startup)(WORD, LPWSADATA);
+ setfunc(startup, WSAStartup, int(PASCAL FAR*)(WORD, LPWSADATA));
+
+ // startup network library
+ WORD version = MAKEWORD(2 /*major*/, 2 /*minor*/);
+ WSADATA data;
+ int err = startup(version, &data);
+ if (data.wVersion != version) {
+ throw XArchNetworkSupport(new XArchEvalWinsock(err));
+ }
+ if (err != 0) {
+ // some other initialization error
+ throwError(err);
+ }
+
+ // get function addresses
+ setfunc(accept_winsock, accept, SOCKET (PASCAL FAR *)(SOCKET s, struct sockaddr FAR *addr, int FAR *addrlen));
+ setfunc(bind_winsock, bind, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *addr, int namelen));
+ setfunc(close_winsock, closesocket, int (PASCAL FAR *)(SOCKET s));
+ setfunc(connect_winsock, connect, int (PASCAL FAR *)(SOCKET s, const struct sockaddr FAR *name, int namelen));
+ setfunc(gethostname_winsock, gethostname, int (PASCAL FAR *)(char FAR * name, int namelen));
+ setfunc(getsockerror_winsock, WSAGetLastError, int (PASCAL FAR *)(void));
+ setfunc(getsockopt_winsock, getsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, void FAR * optval, int FAR *optlen));
+ setfunc(htons_winsock, htons, u_short (PASCAL FAR *)(u_short v));
+ setfunc(inet_ntoa_winsock, inet_ntoa, char FAR * (PASCAL FAR *)(struct in_addr in));
+ setfunc(inet_addr_winsock, inet_addr, unsigned long (PASCAL FAR *)(const char FAR * cp));
+ setfunc(ioctl_winsock, ioctlsocket, int (PASCAL FAR *)(SOCKET s, int cmd, void FAR *));
+ setfunc(listen_winsock, listen, int (PASCAL FAR *)(SOCKET s, int backlog));
+ setfunc(ntohs_winsock, ntohs, u_short (PASCAL FAR *)(u_short v));
+ setfunc(recv_winsock, recv, int (PASCAL FAR *)(SOCKET s, void FAR * buf, int len, int flags));
+ setfunc(select_winsock, select, int (PASCAL FAR *)(int nfds, fd_set FAR *readfds, fd_set FAR *writefds, fd_set FAR *exceptfds, const struct timeval FAR *timeout));
+ setfunc(send_winsock, send, int (PASCAL FAR *)(SOCKET s, const void FAR * buf, int len, int flags));
+ setfunc(setsockopt_winsock, setsockopt, int (PASCAL FAR *)(SOCKET s, int level, int optname, const void FAR * optval, int optlen));
+ setfunc(shutdown_winsock, shutdown, int (PASCAL FAR *)(SOCKET s, int how));
+ setfunc(socket_winsock, socket, SOCKET (PASCAL FAR *)(int af, int type, int protocol));
+ setfunc(gethostbyaddr_winsock, gethostbyaddr, struct hostent FAR * (PASCAL FAR *)(const char FAR * addr, int len, int type));
+ setfunc(gethostbyname_winsock, gethostbyname, struct hostent FAR * (PASCAL FAR *)(const char FAR * name));
+ setfunc(WSACleanup_winsock, WSACleanup, int (PASCAL FAR *)(void));
+ setfunc(WSAFDIsSet_winsock, __WSAFDIsSet, int (PASCAL FAR *)(SOCKET, fd_set FAR *));
+ setfunc(WSACreateEvent_winsock, WSACreateEvent, WSAEVENT (PASCAL FAR *)(void));
+ setfunc(WSACloseEvent_winsock, WSACloseEvent, BOOL (PASCAL FAR *)(WSAEVENT));
+ setfunc(WSASetEvent_winsock, WSASetEvent, BOOL (PASCAL FAR *)(WSAEVENT));
+ setfunc(WSAResetEvent_winsock, WSAResetEvent, BOOL (PASCAL FAR *)(WSAEVENT));
+ setfunc(WSAEventSelect_winsock, WSAEventSelect, int (PASCAL FAR *)(SOCKET, WSAEVENT, long));
+ setfunc(WSAWaitForMultipleEvents_winsock, WSAWaitForMultipleEvents, DWORD (PASCAL FAR *)(DWORD, const WSAEVENT FAR*, BOOL, DWORD, BOOL));
+ setfunc(WSAEnumNetworkEvents_winsock, WSAEnumNetworkEvents, int (PASCAL FAR *)(SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
+
+ s_networkModule = module;
+}
+
+ArchSocket
+ArchNetworkWinsock::newSocket(EAddressFamily family, ESocketType type)
+{
+ // create socket
+ SOCKET fd = socket_winsock(s_family[family], s_type[type], 0);
+ if (fd == INVALID_SOCKET) {
+ throwError(getsockerror_winsock());
+ }
+ try {
+ setBlockingOnSocket(fd, false);
+ BOOL flag = 0;
+ int size = sizeof(flag);
+ if (setsockopt_winsock(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flag, size) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+ }
+ catch (...) {
+ close_winsock(fd);
+ throw;
+ }
+
+ // allocate socket object
+ ArchSocketImpl* socket = new ArchSocketImpl;
+ socket->m_socket = fd;
+ socket->m_refCount = 1;
+ socket->m_event = WSACreateEvent_winsock();
+ socket->m_pollWrite = true;
+ return socket;
+}
+
+ArchSocket
+ArchNetworkWinsock::copySocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // ref the socket and return it
+ ARCH->lockMutex(m_mutex);
+ ++s->m_refCount;
+ ARCH->unlockMutex(m_mutex);
+ return s;
+}
+
+void
+ArchNetworkWinsock::closeSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // unref the socket and note if it should be released
+ ARCH->lockMutex(m_mutex);
+ const bool doClose = (--s->m_refCount == 0);
+ ARCH->unlockMutex(m_mutex);
+
+ // close the socket if necessary
+ if (doClose) {
+ if (close_winsock(s->m_socket) == SOCKET_ERROR) {
+ // close failed. restore the last ref and throw.
+ int err = getsockerror_winsock();
+ ARCH->lockMutex(m_mutex);
+ ++s->m_refCount;
+ ARCH->unlockMutex(m_mutex);
+ throwError(err);
+ }
+ WSACloseEvent_winsock(s->m_event);
+ delete s;
+ }
+}
+
+void
+ArchNetworkWinsock::closeSocketForRead(ArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown_winsock(s->m_socket, SD_RECEIVE) == SOCKET_ERROR) {
+ if (getsockerror_winsock() != WSAENOTCONN) {
+ throwError(getsockerror_winsock());
+ }
+ }
+}
+
+void
+ArchNetworkWinsock::closeSocketForWrite(ArchSocket s)
+{
+ assert(s != NULL);
+
+ if (shutdown_winsock(s->m_socket, SD_SEND) == SOCKET_ERROR) {
+ if (getsockerror_winsock() != WSAENOTCONN) {
+ throwError(getsockerror_winsock());
+ }
+ }
+}
+
+void
+ArchNetworkWinsock::bindSocket(ArchSocket s, ArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (bind_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, addr), addr->m_len) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+}
+
+void
+ArchNetworkWinsock::listenOnSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // hardcoding backlog
+ if (listen_winsock(s->m_socket, 3) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+}
+
+ArchSocket
+ArchNetworkWinsock::acceptSocket(ArchSocket s, ArchNetAddress* const addr)
+{
+ assert(s != NULL);
+
+ // create new socket and temporary address
+ ArchSocketImpl* socket = new ArchSocketImpl;
+ ArchNetAddress tmp = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in6));
+
+ // accept on socket
+ SOCKET fd = accept_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, tmp), &tmp->m_len);
+ if (fd == INVALID_SOCKET) {
+ int err = getsockerror_winsock();
+ delete socket;
+ free(tmp);
+ if (addr) {
+ *addr = NULL;
+ }
+ if (err == WSAEWOULDBLOCK) {
+ return NULL;
+ }
+ throwError(err);
+ }
+
+ try {
+ setBlockingOnSocket(fd, false);
+ }
+ catch (...) {
+ close_winsock(fd);
+ delete socket;
+ free(tmp);
+ if (addr) {
+ *addr = NULL;
+ }
+ throw;
+ }
+
+ // initialize socket
+ socket->m_socket = fd;
+ socket->m_refCount = 1;
+ socket->m_event = WSACreateEvent_winsock();
+ socket->m_pollWrite = true;
+
+ // copy address if requested
+ if (addr != NULL) {
+ *addr = ARCH->copyAddr(tmp);
+ }
+
+ free(tmp);
+ return socket;
+}
+
+bool
+ArchNetworkWinsock::connectSocket(ArchSocket s, ArchNetAddress addr)
+{
+ assert(s != NULL);
+ assert(addr != NULL);
+
+ if (connect_winsock(s->m_socket, TYPED_ADDR(struct sockaddr, addr),
+ addr->m_len) == SOCKET_ERROR) {
+ if (getsockerror_winsock() == WSAEISCONN) {
+ return true;
+ }
+ if (getsockerror_winsock() == WSAEWOULDBLOCK) {
+ return false;
+ }
+ throwError(getsockerror_winsock());
+ }
+ return true;
+}
+
+int
+ArchNetworkWinsock::pollSocket(PollEntry pe[], int num, double timeout)
+{
+ int i;
+ DWORD n;
+
+ // prepare sockets and wait list
+ bool canWrite = false;
+ WSAEVENT* events = (WSAEVENT*)alloca((num + 1) * sizeof(WSAEVENT));
+ for (i = 0, n = 0; i < num; ++i) {
+ // reset return flags
+ pe[i].m_revents = 0;
+
+ // set invalid flag if socket is bogus then go to next socket
+ if (pe[i].m_socket == NULL) {
+ pe[i].m_revents |= kPOLLNVAL;
+ continue;
+ }
+
+ // select desired events
+ long socketEvents = 0;
+ if ((pe[i].m_events & kPOLLIN) != 0) {
+ socketEvents |= FD_READ | FD_ACCEPT | FD_CLOSE;
+ }
+ if ((pe[i].m_events & kPOLLOUT) != 0) {
+ socketEvents |= FD_WRITE | FD_CONNECT | FD_CLOSE;
+
+ // if m_pollWrite is false then we assume the socket is
+ // writable. winsock doesn't signal writability except
+ // when the state changes from unwritable.
+ if (!pe[i].m_socket->m_pollWrite) {
+ canWrite = true;
+ pe[i].m_revents |= kPOLLOUT;
+ }
+ }
+
+ // if no events then ignore socket
+ if (socketEvents == 0) {
+ continue;
+ }
+
+ // select socket for desired events
+ WSAEventSelect_winsock(pe[i].m_socket->m_socket,
+ pe[i].m_socket->m_event, socketEvents);
+
+ // add socket event to wait list
+ events[n++] = pe[i].m_socket->m_event;
+ }
+
+ // if no sockets then return immediately
+ if (n == 0) {
+ return 0;
+ }
+
+ // add the unblock event
+ ArchMultithreadWindows* mt = ArchMultithreadWindows::getInstance();
+ ArchThread thread = mt->newCurrentThread();
+ WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
+ ARCH->closeThread(thread);
+ if (unblockEvent == NULL) {
+ unblockEvent = new WSAEVENT;
+ m_unblockEvents.push_back(unblockEvent);
+ *unblockEvent = WSACreateEvent_winsock();
+ mt->setNetworkDataForCurrentThread(unblockEvent);
+ }
+ events[n++] = *unblockEvent;
+
+ // prepare timeout
+ DWORD t = (timeout < 0.0) ? INFINITE : (DWORD)(1000.0 * timeout);
+ if (canWrite) {
+ // if we know we can write then don't block
+ t = 0;
+ }
+
+ // wait
+ DWORD result = WSAWaitForMultipleEvents_winsock(n, events, FALSE, t, FALSE);
+
+ // reset the unblock event
+ WSAResetEvent_winsock(*unblockEvent);
+
+ // handle results
+ if (result == WSA_WAIT_FAILED) {
+ if (getsockerror_winsock() == WSAEINTR) {
+ // interrupted system call
+ ARCH->testCancelThread();
+ return 0;
+ }
+ throwError(getsockerror_winsock());
+ }
+ if (result == WSA_WAIT_TIMEOUT && !canWrite) {
+ return 0;
+ }
+ if (result == WSA_WAIT_EVENT_0 + n - 1) {
+ // the unblock event was signalled
+ return 0;
+ }
+ for (i = 0, n = 0; i < num; ++i) {
+ // skip events we didn't check
+ if (pe[i].m_socket == NULL ||
+ (pe[i].m_events & (kPOLLIN | kPOLLOUT)) == 0) {
+ continue;
+ }
+
+ // get events
+ WSANETWORKEVENTS info;
+ if (WSAEnumNetworkEvents_winsock(pe[i].m_socket->m_socket,
+ pe[i].m_socket->m_event, &info) == SOCKET_ERROR) {
+ continue;
+ }
+ if ((info.lNetworkEvents & FD_READ) != 0) {
+ pe[i].m_revents |= kPOLLIN;
+ }
+ if ((info.lNetworkEvents & FD_ACCEPT) != 0) {
+ pe[i].m_revents |= kPOLLIN;
+ }
+ if ((info.lNetworkEvents & FD_WRITE) != 0) {
+ pe[i].m_revents |= kPOLLOUT;
+
+ // socket is now writable so don't bothing polling for
+ // writable until it becomes unwritable.
+ pe[i].m_socket->m_pollWrite = false;
+ }
+ if ((info.lNetworkEvents & FD_CONNECT) != 0) {
+ if (info.iErrorCode[FD_CONNECT_BIT] != 0) {
+ pe[i].m_revents |= kPOLLERR;
+ }
+ else {
+ pe[i].m_revents |= kPOLLOUT;
+ pe[i].m_socket->m_pollWrite = false;
+ }
+ }
+ if ((info.lNetworkEvents & FD_CLOSE) != 0) {
+ if (info.iErrorCode[FD_CLOSE_BIT] != 0) {
+ pe[i].m_revents |= kPOLLERR;
+ }
+ else {
+ if ((pe[i].m_events & kPOLLIN) != 0) {
+ pe[i].m_revents |= kPOLLIN;
+ }
+ if ((pe[i].m_events & kPOLLOUT) != 0) {
+ pe[i].m_revents |= kPOLLOUT;
+ }
+ }
+ }
+ if (pe[i].m_revents != 0) {
+ ++n;
+ }
+ }
+
+ return (int)n;
+}
+
+void
+ArchNetworkWinsock::unblockPollSocket(ArchThread thread)
+{
+ // set the unblock event
+ ArchMultithreadWindows* mt = ArchMultithreadWindows::getInstance();
+ WSAEVENT* unblockEvent = (WSAEVENT*)mt->getNetworkDataForThread(thread);
+ if (unblockEvent != NULL) {
+ WSASetEvent_winsock(*unblockEvent);
+ }
+}
+
+size_t
+ArchNetworkWinsock::readSocket(ArchSocket s, void* buf, size_t len)
+{
+ assert(s != NULL);
+
+ int n = recv_winsock(s->m_socket, buf, (int)len, 0);
+ if (n == SOCKET_ERROR) {
+ int err = getsockerror_winsock();
+ if (err == WSAEINTR || err == WSAEWOULDBLOCK) {
+ return 0;
+ }
+ throwError(err);
+ }
+ return static_cast<size_t>(n);
+}
+
+size_t
+ArchNetworkWinsock::writeSocket(ArchSocket s, const void* buf, size_t len)
+{
+ assert(s != NULL);
+
+ int n = send_winsock(s->m_socket, buf, (int)len, 0);
+ if (n == SOCKET_ERROR) {
+ int err = getsockerror_winsock();
+ if (err == WSAEINTR) {
+ return 0;
+ }
+ if (err == WSAEWOULDBLOCK) {
+ s->m_pollWrite = true;
+ return 0;
+ }
+ throwError(err);
+ }
+ return static_cast<size_t>(n);
+}
+
+void
+ArchNetworkWinsock::throwErrorOnSocket(ArchSocket s)
+{
+ assert(s != NULL);
+
+ // get the error from the socket layer
+ int err = 0;
+ int size = sizeof(err);
+ if (getsockopt_winsock(s->m_socket, SOL_SOCKET,
+ SO_ERROR, &err, &size) == SOCKET_ERROR) {
+ err = getsockerror_winsock();
+ }
+
+ // throw if there's an error
+ if (err != 0) {
+ throwError(err);
+ }
+}
+
+void
+ArchNetworkWinsock::setBlockingOnSocket(SOCKET s, bool blocking)
+{
+ assert(s != 0);
+
+ int flag = blocking ? 0 : 1;
+ if (ioctl_winsock(s, FIONBIO, &flag) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+}
+
+bool
+ArchNetworkWinsock::setNoDelayOnSocket(ArchSocket s, bool noDelay)
+{
+ assert(s != NULL);
+
+ // get old state
+ BOOL oflag;
+ int size = sizeof(oflag);
+ if (getsockopt_winsock(s->m_socket, IPPROTO_TCP,
+ TCP_NODELAY, &oflag, &size) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+
+ // set new state
+ BOOL flag = noDelay ? 1 : 0;
+ size = sizeof(flag);
+ if (setsockopt_winsock(s->m_socket, IPPROTO_TCP,
+ TCP_NODELAY, &flag, size) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+
+ return (oflag != 0);
+}
+
+bool
+ArchNetworkWinsock::setReuseAddrOnSocket(ArchSocket s, bool reuse)
+{
+ assert(s != NULL);
+
+ // get old state
+ BOOL oflag;
+ int size = sizeof(oflag);
+ if (getsockopt_winsock(s->m_socket, SOL_SOCKET,
+ SO_REUSEADDR, &oflag, &size) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+
+ // set new state
+ BOOL flag = reuse ? 1 : 0;
+ size = sizeof(flag);
+ if (setsockopt_winsock(s->m_socket, SOL_SOCKET,
+ SO_REUSEADDR, &flag, size) == SOCKET_ERROR) {
+ throwError(getsockerror_winsock());
+ }
+
+ return (oflag != 0);
+}
+
+std::string
+ArchNetworkWinsock::getHostName()
+{
+ char name[256];
+ if (gethostname_winsock(name, sizeof(name)) == -1) {
+ name[0] = '\0';
+ }
+ else {
+ name[sizeof(name) - 1] = '\0';
+ }
+ return name;
+}
+
+ArchNetAddress
+ArchNetworkWinsock::newAnyAddr(EAddressFamily family)
+{
+ ArchNetAddressImpl* addr = NULL;
+ switch (family) {
+ case kINET: {
+ addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in));
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ipAddr->sin_family = AF_INET;
+ ipAddr->sin_port = 0;
+ ipAddr->sin_addr.s_addr = INADDR_ANY;
+ break;
+ }
+
+ case kINET6: {
+ addr = ArchNetAddressImpl::alloc(sizeof(struct sockaddr_in6));
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ ipAddr->sin6_family = AF_INET6;
+ ipAddr->sin6_port = 0;
+ memcpy(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any));
+ break;
+ }
+
+ default:
+ assert(0 && "invalid family");
+ }
+ return addr;
+}
+
+ArchNetAddress
+ArchNetworkWinsock::copyAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ ArchNetAddressImpl* copy = ArchNetAddressImpl::alloc(addr->m_len);
+ memcpy(TYPED_ADDR(void, copy), TYPED_ADDR(void, addr), addr->m_len);
+ return copy;
+}
+
+ArchNetAddress
+ArchNetworkWinsock::nameToAddr(const std::string& name)
+{
+ // allocate address
+
+ ArchNetAddressImpl* addr = new ArchNetAddressImpl;
+
+ struct addrinfo hints;
+ struct addrinfo *p;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ int ret = -1;
+
+ ARCH->lockMutex(m_mutex);
+ if ((ret = getaddrinfo(name.c_str(), NULL, &hints, &p)) != 0) {
+ ARCH->unlockMutex(m_mutex);
+ delete addr;
+ throwNameError(ret);
+ }
+
+ if (p->ai_family == AF_INET) {
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in);
+ } else {
+ addr->m_len = (socklen_t)sizeof(struct sockaddr_in6);
+ }
+
+ memcpy(&addr->m_addr, p->ai_addr, addr->m_len);
+ freeaddrinfo(p);
+ ARCH->unlockMutex(m_mutex);
+ return addr;
+}
+
+void
+ArchNetworkWinsock::closeAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ free(addr);
+}
+
+std::string
+ArchNetworkWinsock::addrToName(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ char host[1024];
+ char service[20];
+ int ret = getnameinfo(TYPED_ADDR(struct sockaddr, addr), addr->m_len, host, sizeof(host), service, sizeof(service), 0);
+
+ if (ret != NULL) {
+ throwNameError(ret);
+ }
+
+ // return (primary) name
+ std::string name = host;
+ return name;
+}
+
+std::string
+ArchNetworkWinsock::addrToString(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ return inet_ntoa_winsock(ipAddr->sin_addr);
+ }
+
+ case kINET6: {
+ char strAddr[INET6_ADDRSTRLEN];
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ inet_ntop(AF_INET6, &ipAddr->sin6_addr, strAddr, INET6_ADDRSTRLEN);
+ return strAddr;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return "";
+ }
+}
+
+IArchNetwork::EAddressFamily
+ArchNetworkWinsock::getAddrFamily(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (addr->m_addr.ss_family) {
+ case AF_INET:
+ return kINET;
+
+ case AF_INET6:
+ return kINET6;
+
+ default:
+ return kUNKNOWN;
+ }
+}
+
+void
+ArchNetworkWinsock::setAddrPort(ArchNetAddress addr, int port)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ ipAddr->sin_port = htons_winsock(static_cast<u_short>(port));
+ break;
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ ipAddr->sin6_port = htons_winsock(static_cast<u_short>(port));
+ break;
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ break;
+ }
+}
+
+int
+ArchNetworkWinsock::getAddrPort(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ return ntohs_winsock(ipAddr->sin_port);
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ return ntohs_winsock(ipAddr->sin6_port);
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return 0;
+ }
+}
+
+bool
+ArchNetworkWinsock::isAnyAddr(ArchNetAddress addr)
+{
+ assert(addr != NULL);
+
+ switch (getAddrFamily(addr)) {
+ case kINET: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in, addr);
+ return (addr->m_len == sizeof(struct sockaddr_in) &&
+ ipAddr->sin_addr.s_addr == INADDR_ANY);
+ }
+
+ case kINET6: {
+ auto* ipAddr = TYPED_ADDR(struct sockaddr_in6, addr);
+ return (addr->m_len == sizeof(struct sockaddr_in) &&
+ memcmp(&ipAddr->sin6_addr, &in6addr_any, sizeof(in6addr_any))== 0);
+ }
+
+ default:
+ assert(0 && "unknown address family");
+ return true;
+ }
+}
+
+bool
+ArchNetworkWinsock::isEqualAddr(ArchNetAddress a, ArchNetAddress b)
+{
+ return (a == b || (a->m_len == b->m_len &&
+ memcmp(&a->m_addr, &b->m_addr, a->m_len) == 0));
+}
+
+void
+ArchNetworkWinsock::throwError(int err)
+{
+ switch (err) {
+ case WSAEACCES:
+ throw XArchNetworkAccess(new XArchEvalWinsock(err));
+
+ case WSAEMFILE:
+ case WSAENOBUFS:
+ case WSAENETDOWN:
+ throw XArchNetworkResource(new XArchEvalWinsock(err));
+
+ case WSAEPROTOTYPE:
+ case WSAEPROTONOSUPPORT:
+ case WSAEAFNOSUPPORT:
+ case WSAEPFNOSUPPORT:
+ case WSAESOCKTNOSUPPORT:
+ case WSAEINVAL:
+ case WSAENOPROTOOPT:
+ case WSAEOPNOTSUPP:
+ case WSAESHUTDOWN:
+ case WSANOTINITIALISED:
+ case WSAVERNOTSUPPORTED:
+ case WSASYSNOTREADY:
+ throw XArchNetworkSupport(new XArchEvalWinsock(err));
+
+ case WSAEADDRNOTAVAIL:
+ throw XArchNetworkNoAddress(new XArchEvalWinsock(err));
+
+ case WSAEADDRINUSE:
+ throw XArchNetworkAddressInUse(new XArchEvalWinsock(err));
+
+ case WSAEHOSTUNREACH:
+ case WSAENETUNREACH:
+ throw XArchNetworkNoRoute(new XArchEvalWinsock(err));
+
+ case WSAENOTCONN:
+ throw XArchNetworkNotConnected(new XArchEvalWinsock(err));
+
+ case WSAEDISCON:
+ throw XArchNetworkShutdown(new XArchEvalWinsock(err));
+
+ case WSAENETRESET:
+ case WSAECONNABORTED:
+ case WSAECONNRESET:
+ throw XArchNetworkDisconnected(new XArchEvalWinsock(err));
+
+ case WSAECONNREFUSED:
+ throw XArchNetworkConnectionRefused(new XArchEvalWinsock(err));
+
+ case WSAEHOSTDOWN:
+ case WSAETIMEDOUT:
+ throw XArchNetworkTimedOut(new XArchEvalWinsock(err));
+
+ case WSAHOST_NOT_FOUND:
+ throw XArchNetworkNameUnknown(new XArchEvalWinsock(err));
+
+ case WSANO_DATA:
+ throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err));
+
+ case WSANO_RECOVERY:
+ throw XArchNetworkNameFailure(new XArchEvalWinsock(err));
+
+ case WSATRY_AGAIN:
+ throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err));
+
+ default:
+ throw XArchNetwork(new XArchEvalWinsock(err));
+ }
+}
+
+void
+ArchNetworkWinsock::throwNameError(int err)
+{
+ switch (err) {
+ case WSAHOST_NOT_FOUND:
+ throw XArchNetworkNameUnknown(new XArchEvalWinsock(err));
+
+ case WSANO_DATA:
+ throw XArchNetworkNameNoAddress(new XArchEvalWinsock(err));
+
+ case WSANO_RECOVERY:
+ throw XArchNetworkNameFailure(new XArchEvalWinsock(err));
+
+ case WSATRY_AGAIN:
+ throw XArchNetworkNameUnavailable(new XArchEvalWinsock(err));
+
+ default:
+ throw XArchNetworkName(new XArchEvalWinsock(err));
+ }
+}
diff --git a/src/lib/arch/win32/ArchNetworkWinsock.h b/src/lib/arch/win32/ArchNetworkWinsock.h
new file mode 100644
index 0000000..0b01671
--- /dev/null
+++ b/src/lib/arch/win32/ArchNetworkWinsock.h
@@ -0,0 +1,111 @@
+/*
+ * barrier -- 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 <ws2tcpip.h>
+// declare no functions in winsock2
+#ifndef INCL_WINSOCK_API_PROTOTYPES
+#define INCL_WINSOCK_API_PROTOTYPES 0
+#endif
+#define INCL_WINSOCK_API_TYPEDEFS 0
+
+#include "arch/IArchNetwork.h"
+#include "arch/IArchMultithread.h"
+
+#include <WinSock2.h>
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <list>
+
+#pragma comment(lib, "ws2_32.lib")
+
+#define ARCH_NETWORK ArchNetworkWinsock
+
+class ArchSocketImpl {
+public:
+ SOCKET m_socket;
+ int m_refCount;
+ WSAEVENT m_event;
+ bool m_pollWrite;
+};
+
+class ArchNetAddressImpl {
+public:
+ static ArchNetAddressImpl* alloc(size_t);
+
+public:
+ int m_len;
+ struct sockaddr_storage m_addr;
+};
+#define ADDR_HDR_SIZE offsetof(ArchNetAddressImpl, m_addr)
+#define TYPED_ADDR(type_, addr_) (reinterpret_cast<type_*>(&addr_->m_addr))
+
+//! Win32 implementation of IArchNetwork
+class ArchNetworkWinsock : public IArchNetwork {
+public:
+ ArchNetworkWinsock();
+ virtual ~ArchNetworkWinsock();
+
+ virtual void init();
+
+ // IArchNetwork overrides
+ virtual ArchSocket newSocket(EAddressFamily, ESocketType);
+ virtual ArchSocket copySocket(ArchSocket s);
+ virtual void closeSocket(ArchSocket s);
+ virtual void closeSocketForRead(ArchSocket s);
+ virtual void closeSocketForWrite(ArchSocket s);
+ virtual void bindSocket(ArchSocket s, ArchNetAddress addr);
+ virtual void listenOnSocket(ArchSocket s);
+ virtual ArchSocket acceptSocket(ArchSocket s, ArchNetAddress* addr);
+ virtual bool connectSocket(ArchSocket s, ArchNetAddress name);
+ virtual int pollSocket(PollEntry[], int num, double timeout);
+ virtual void unblockPollSocket(ArchThread thread);
+ virtual size_t readSocket(ArchSocket s, void* buf, size_t len);
+ virtual size_t writeSocket(ArchSocket s,
+ const void* buf, size_t len);
+ virtual void throwErrorOnSocket(ArchSocket);
+ virtual bool setNoDelayOnSocket(ArchSocket, bool noDelay);
+ virtual bool setReuseAddrOnSocket(ArchSocket, bool reuse);
+ virtual std::string getHostName();
+ virtual ArchNetAddress newAnyAddr(EAddressFamily);
+ virtual ArchNetAddress copyAddr(ArchNetAddress);
+ virtual ArchNetAddress nameToAddr(const std::string&);
+ virtual void closeAddr(ArchNetAddress);
+ virtual std::string addrToName(ArchNetAddress);
+ virtual std::string addrToString(ArchNetAddress);
+ virtual EAddressFamily getAddrFamily(ArchNetAddress);
+ virtual void setAddrPort(ArchNetAddress, int port);
+ virtual int getAddrPort(ArchNetAddress);
+ virtual bool isAnyAddr(ArchNetAddress);
+ virtual bool isEqualAddr(ArchNetAddress, ArchNetAddress);
+
+private:
+ void initModule(HMODULE);
+
+ void setBlockingOnSocket(SOCKET, bool blocking);
+
+ void throwError(int);
+ void throwNameError(int);
+
+private:
+ typedef std::list<WSAEVENT> EventList;
+
+ ArchMutex m_mutex;
+ EventList m_unblockEvents;
+};
diff --git a/src/lib/arch/win32/ArchSleepWindows.cpp b/src/lib/arch/win32/ArchSleepWindows.cpp
new file mode 100644
index 0000000..69648a7
--- /dev/null
+++ b/src/lib/arch/win32/ArchSleepWindows.cpp
@@ -0,0 +1,61 @@
+/*
+ * barrier -- 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 "arch/win32/ArchSleepWindows.h"
+#include "arch/Arch.h"
+#include "arch/win32/ArchMultithreadWindows.h"
+
+//
+// ArchSleepWindows
+//
+
+ArchSleepWindows::ArchSleepWindows()
+{
+ // do nothing
+}
+
+ArchSleepWindows::~ArchSleepWindows()
+{
+ // do nothing
+}
+
+void
+ArchSleepWindows::sleep(double timeout)
+{
+ ARCH->testCancelThread();
+ if (timeout < 0.0) {
+ return;
+ }
+
+ // get the cancel event from the current thread. this only
+ // works if we're using the windows multithread object but
+ // this is windows so that's pretty certain; we'll get a
+ // link error if we're not, though.
+ ArchMultithreadWindows* mt = ArchMultithreadWindows::getInstance();
+ if (mt != NULL) {
+ HANDLE cancelEvent = mt->getCancelEventForCurrentThread();
+ WaitForSingleObject(cancelEvent, (DWORD)(1000.0 * timeout));
+ if (timeout == 0.0) {
+ Sleep(0);
+ }
+ }
+ else {
+ Sleep((DWORD)(1000.0 * timeout));
+ }
+ ARCH->testCancelThread();
+}
diff --git a/src/lib/arch/win32/ArchSleepWindows.h b/src/lib/arch/win32/ArchSleepWindows.h
new file mode 100644
index 0000000..d673caf
--- /dev/null
+++ b/src/lib/arch/win32/ArchSleepWindows.h
@@ -0,0 +1,33 @@
+/*
+ * barrier -- 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 "arch/IArchSleep.h"
+
+#define ARCH_SLEEP ArchSleepWindows
+
+//! Win32 implementation of IArchSleep
+class ArchSleepWindows : public IArchSleep {
+public:
+ ArchSleepWindows();
+ virtual ~ArchSleepWindows();
+
+ // IArchSleep overrides
+ virtual void sleep(double timeout);
+};
diff --git a/src/lib/arch/win32/ArchStringWindows.cpp b/src/lib/arch/win32/ArchStringWindows.cpp
new file mode 100644
index 0000000..deaf536
--- /dev/null
+++ b/src/lib/arch/win32/ArchStringWindows.cpp
@@ -0,0 +1,46 @@
+/*
+ * barrier -- 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 "arch/win32/ArchStringWindows.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <stdio.h>
+
+//
+// ArchStringWindows
+//
+
+#include "arch/multibyte.h"
+#define HAVE_VSNPRINTF 1
+#define ARCH_VSNPRINTF _vsnprintf
+#include "arch/vsnprintf.h"
+
+ArchStringWindows::ArchStringWindows()
+{
+}
+
+ArchStringWindows::~ArchStringWindows()
+{
+}
+
+IArchString::EWideCharEncoding
+ArchStringWindows::getWideCharEncoding()
+{
+ return kUTF16;
+}
diff --git a/src/lib/arch/win32/ArchStringWindows.h b/src/lib/arch/win32/ArchStringWindows.h
new file mode 100644
index 0000000..23812dc
--- /dev/null
+++ b/src/lib/arch/win32/ArchStringWindows.h
@@ -0,0 +1,34 @@
+/*
+ * barrier -- 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 "arch/IArchString.h"
+
+#define ARCH_STRING ArchStringWindows
+
+//! Win32 implementation of IArchString
+class ArchStringWindows : public IArchString {
+public:
+ ArchStringWindows();
+ virtual ~ArchStringWindows();
+
+ // IArchString overrides
+ virtual EWideCharEncoding
+ getWideCharEncoding();
+};
diff --git a/src/lib/arch/win32/ArchSystemWindows.cpp b/src/lib/arch/win32/ArchSystemWindows.cpp
new file mode 100644
index 0000000..cf3b066
--- /dev/null
+++ b/src/lib/arch/win32/ArchSystemWindows.cpp
@@ -0,0 +1,166 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "arch/win32/ArchSystemWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/win32/XArchWindows.h"
+
+#include "tchar.h"
+#include <string>
+
+#include <windows.h>
+#include <psapi.h>
+
+static const char* s_settingsKeyNames[] = {
+ _T("SOFTWARE"),
+ _T("Barrier"),
+ NULL
+};
+
+//
+// ArchSystemWindows
+//
+
+ArchSystemWindows::ArchSystemWindows()
+{
+ // do nothing
+}
+
+ArchSystemWindows::~ArchSystemWindows()
+{
+ // do nothing
+}
+
+std::string
+ArchSystemWindows::getOSName() const
+{
+ std::string osName ("Microsoft Windows <unknown>");
+ static const TCHAR* const windowsVersionKeyNames[] = {
+ _T("SOFTWARE"),
+ _T("Microsoft"),
+ _T("Windows NT"),
+ _T("CurrentVersion"),
+ NULL
+ };
+
+ HKEY key = ArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, windowsVersionKeyNames);
+ if (key == NULL) {
+ return osName;
+ }
+
+ std::string productName = ArchMiscWindows::readValueString(key, "ProductName");
+ if (osName.empty()) {
+ return osName;
+ }
+
+ return "Microsoft " + productName;
+}
+
+std::string
+ArchSystemWindows::getPlatformName() const
+{
+#ifdef _X86_
+ if (isWOW64())
+ return "x86 (WOW64)";
+ else
+ return "x86";
+#else
+#ifdef _AMD64_
+ return "x64";
+#else
+ return "Unknown";
+#endif
+#endif
+}
+
+std::string
+ArchSystemWindows::setting(const std::string& valueName) const
+{
+ HKEY key = ArchMiscWindows::openKey(HKEY_LOCAL_MACHINE, s_settingsKeyNames);
+ if (key == NULL)
+ return "";
+
+ return ArchMiscWindows::readValueString(key, valueName.c_str());
+}
+
+void
+ArchSystemWindows::setting(const std::string& valueName, const std::string& valueString) const
+{
+ HKEY key = ArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_settingsKeyNames);
+ if (key == NULL)
+ throw XArch(std::string("could not access registry key: ") + valueName);
+ ArchMiscWindows::setValue(key, valueName.c_str(), valueString.c_str());
+}
+
+bool
+ArchSystemWindows::isWOW64() const
+{
+#if WINVER >= _WIN32_WINNT_WINXP
+ typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
+ HMODULE hModule = GetModuleHandle(TEXT("kernel32"));
+ if (!hModule) return FALSE;
+
+ LPFN_ISWOW64PROCESS fnIsWow64Process =
+ (LPFN_ISWOW64PROCESS) GetProcAddress(hModule, "IsWow64Process");
+
+ BOOL bIsWow64 = FALSE;
+ if (NULL != fnIsWow64Process &&
+ fnIsWow64Process(GetCurrentProcess(), &bIsWow64) &&
+ bIsWow64)
+ {
+ return true;
+ }
+#endif
+ return false;
+}
+#pragma comment(lib, "psapi")
+
+std::string
+ArchSystemWindows::getLibsUsed(void) const
+{
+ HMODULE hMods[1024];
+ HANDLE hProcess;
+ DWORD cbNeeded;
+ unsigned int i;
+ char hex[16];
+
+ DWORD pid = GetCurrentProcessId();
+
+ std::string msg = "pid:" + std::to_string((unsigned long long)pid) + "\n";
+
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
+
+ if (NULL == hProcess) {
+ return msg;
+ }
+
+ if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
+ for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
+ TCHAR szModName[MAX_PATH];
+ if (GetModuleFileNameEx(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) {
+ sprintf(hex, "(0x%08llX)", reinterpret_cast<long long>(hMods[i]));
+ msg += szModName;
+ msg.append(hex);
+ msg.append("\n");
+ }
+ }
+ }
+
+ CloseHandle(hProcess);
+ return msg;
+}
diff --git a/src/lib/arch/win32/ArchSystemWindows.h b/src/lib/arch/win32/ArchSystemWindows.h
new file mode 100644
index 0000000..3d45ee6
--- /dev/null
+++ b/src/lib/arch/win32/ArchSystemWindows.h
@@ -0,0 +1,39 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "arch/IArchSystem.h"
+
+#define ARCH_SYSTEM ArchSystemWindows
+
+//! Win32 implementation of IArchString
+class ArchSystemWindows : public IArchSystem {
+public:
+ ArchSystemWindows();
+ virtual ~ArchSystemWindows();
+
+ // IArchSystem overrides
+ virtual std::string getOSName() const;
+ virtual std::string getPlatformName() const;
+ virtual std::string setting(const std::string& valueName) const;
+ virtual void setting(const std::string& valueName, const std::string& valueString) const;
+ virtual std::string getLibsUsed(void) const;
+
+ bool isWOW64() const;
+};
diff --git a/src/lib/arch/win32/ArchTaskBarWindows.cpp b/src/lib/arch/win32/ArchTaskBarWindows.cpp
new file mode 100644
index 0000000..731dc59
--- /dev/null
+++ b/src/lib/arch/win32/ArchTaskBarWindows.cpp
@@ -0,0 +1,514 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "arch/win32/ArchTaskBarWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+#include "barrier/win32/AppUtilWindows.h"
+
+#include <string.h>
+#include <shellapi.h>
+
+static const UINT kAddReceiver = WM_USER + 10;
+static const UINT kRemoveReceiver = WM_USER + 11;
+static const UINT kUpdateReceiver = WM_USER + 12;
+static const UINT kNotifyReceiver = WM_USER + 13;
+static const UINT kFirstReceiverID = WM_USER + 14;
+
+//
+// ArchTaskBarWindows
+//
+
+ArchTaskBarWindows* ArchTaskBarWindows::s_instance = NULL;
+
+ArchTaskBarWindows::ArchTaskBarWindows() :
+ m_mutex(NULL),
+ m_condVar(NULL),
+ m_ready(false),
+ m_result(0),
+ m_thread(NULL),
+ m_hwnd(NULL),
+ m_taskBarRestart(0),
+ m_nextID(kFirstReceiverID)
+{
+ // save the singleton instance
+ s_instance = this;
+}
+
+ArchTaskBarWindows::~ArchTaskBarWindows()
+{
+ if (m_thread != NULL) {
+ PostMessage(m_hwnd, WM_QUIT, 0, 0);
+ ARCH->wait(m_thread, -1.0);
+ ARCH->closeThread(m_thread);
+ }
+ if (m_condVar != NULL) {
+ ARCH->closeCondVar(m_condVar);
+ }
+ if (m_mutex != NULL) {
+ ARCH->closeMutex(m_mutex);
+ }
+ s_instance = NULL;
+}
+
+void
+ArchTaskBarWindows::init()
+{
+ // we need a mutex
+ m_mutex = ARCH->newMutex();
+
+ // and a condition variable which uses the above mutex
+ m_ready = false;
+ m_condVar = ARCH->newCondVar();
+
+ // we're going to want to get a result from the thread we're
+ // about to create to know if it initialized successfully.
+ // so we lock the condition variable.
+ ARCH->lockMutex(m_mutex);
+
+ // open a window and run an event loop in a separate thread.
+ // this has to happen in a separate thread because if we
+ // create a window on the current desktop with the current
+ // thread then the current thread won't be able to switch
+ // desktops if it needs to.
+ m_thread = ARCH->newThread(&ArchTaskBarWindows::threadEntry, this);
+
+ // wait for child thread
+ while (!m_ready) {
+ ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
+ }
+
+ // ready
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::addDialog(HWND hwnd)
+{
+ ArchMiscWindows::addDialog(hwnd);
+}
+
+void
+ArchTaskBarWindows::removeDialog(HWND hwnd)
+{
+ ArchMiscWindows::removeDialog(hwnd);
+}
+
+void
+ArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver)
+{
+ // ignore bogus receiver
+ if (receiver == NULL) {
+ return;
+ }
+
+ // add receiver if necessary
+ ReceiverToInfoMap::iterator index = m_receivers.find(receiver);
+ if (index == m_receivers.end()) {
+ // add it, creating a new message ID for it
+ ReceiverInfo info;
+ info.m_id = getNextID();
+ index = m_receivers.insert(std::make_pair(receiver, info)).first;
+
+ // add ID to receiver mapping
+ m_idTable.insert(std::make_pair(info.m_id, index));
+ }
+
+ // add receiver
+ PostMessage(m_hwnd, kAddReceiver, index->second.m_id, 0);
+}
+
+void
+ArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver)
+{
+ // find receiver
+ ReceiverToInfoMap::iterator index = m_receivers.find(receiver);
+ if (index == m_receivers.end()) {
+ return;
+ }
+
+ // remove icon. wait for this to finish before returning.
+ SendMessage(m_hwnd, kRemoveReceiver, index->second.m_id, 0);
+
+ // recycle the ID
+ recycleID(index->second.m_id);
+
+ // discard
+ m_idTable.erase(index->second.m_id);
+ m_receivers.erase(index);
+}
+
+void
+ArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver)
+{
+ // find receiver
+ ReceiverToInfoMap::const_iterator index = m_receivers.find(receiver);
+ if (index == m_receivers.end()) {
+ return;
+ }
+
+ // update icon and tool tip
+ PostMessage(m_hwnd, kUpdateReceiver, index->second.m_id, 0);
+}
+
+UINT
+ArchTaskBarWindows::getNextID()
+{
+ if (m_oldIDs.empty()) {
+ return m_nextID++;
+ }
+ UINT id = m_oldIDs.back();
+ m_oldIDs.pop_back();
+ return id;
+}
+
+void
+ArchTaskBarWindows::recycleID(UINT id)
+{
+ m_oldIDs.push_back(id);
+}
+
+void
+ArchTaskBarWindows::addIcon(UINT id)
+{
+ ARCH->lockMutex(m_mutex);
+ CIDToReceiverMap::const_iterator index = m_idTable.find(id);
+ if (index != m_idTable.end()) {
+ modifyIconNoLock(index->second, NIM_ADD);
+ }
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::removeIcon(UINT id)
+{
+ ARCH->lockMutex(m_mutex);
+ removeIconNoLock(id);
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::updateIcon(UINT id)
+{
+ ARCH->lockMutex(m_mutex);
+ CIDToReceiverMap::const_iterator index = m_idTable.find(id);
+ if (index != m_idTable.end()) {
+ modifyIconNoLock(index->second, NIM_MODIFY);
+ }
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::addAllIcons()
+{
+ ARCH->lockMutex(m_mutex);
+ for (ReceiverToInfoMap::const_iterator index = m_receivers.begin();
+ index != m_receivers.end(); ++index) {
+ modifyIconNoLock(index, NIM_ADD);
+ }
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::removeAllIcons()
+{
+ ARCH->lockMutex(m_mutex);
+ for (ReceiverToInfoMap::const_iterator index = m_receivers.begin();
+ index != m_receivers.end(); ++index) {
+ removeIconNoLock(index->second.m_id);
+ }
+ ARCH->unlockMutex(m_mutex);
+}
+
+void
+ArchTaskBarWindows::modifyIconNoLock(
+ ReceiverToInfoMap::const_iterator index, DWORD taskBarMessage)
+{
+ // get receiver
+ UINT id = index->second.m_id;
+ IArchTaskBarReceiver* receiver = index->first;
+
+ // lock receiver so icon and tool tip are guaranteed to be consistent
+ receiver->lock();
+
+ // get icon data
+ HICON icon = static_cast<HICON>(
+ const_cast<IArchTaskBarReceiver::Icon>(receiver->getIcon()));
+
+ // get tool tip
+ std::string toolTip = receiver->getToolTip();
+
+ // done querying
+ receiver->unlock();
+
+ // prepare to add icon
+ NOTIFYICONDATA data;
+ data.cbSize = sizeof(NOTIFYICONDATA);
+ data.hWnd = m_hwnd;
+ data.uID = id;
+ data.uFlags = NIF_MESSAGE;
+ data.uCallbackMessage = kNotifyReceiver;
+ data.hIcon = icon;
+ if (icon != NULL) {
+ data.uFlags |= NIF_ICON;
+ }
+ if (!toolTip.empty()) {
+ strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip));
+ data.szTip[sizeof(data.szTip) - 1] = '\0';
+ data.uFlags |= NIF_TIP;
+ }
+ else {
+ data.szTip[0] = '\0';
+ }
+
+ // add icon
+ if (Shell_NotifyIcon(taskBarMessage, &data) == 0) {
+ // failed
+ }
+}
+
+void
+ArchTaskBarWindows::removeIconNoLock(UINT id)
+{
+ NOTIFYICONDATA data;
+ data.cbSize = sizeof(NOTIFYICONDATA);
+ data.hWnd = m_hwnd;
+ data.uID = id;
+ if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) {
+ // failed
+ }
+}
+
+void
+ArchTaskBarWindows::handleIconMessage(
+ IArchTaskBarReceiver* receiver, LPARAM lParam)
+{
+ // process message
+ switch (lParam) {
+ case WM_LBUTTONDOWN:
+ receiver->showStatus();
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ receiver->primaryAction();
+ break;
+
+ case WM_RBUTTONUP: {
+ POINT p;
+ GetCursorPos(&p);
+ receiver->runMenu(p.x, p.y);
+ break;
+ }
+
+ case WM_MOUSEMOVE:
+ // currently unused
+ break;
+
+ default:
+ // unused
+ break;
+ }
+}
+
+bool
+ArchTaskBarWindows::processDialogs(MSG* msg)
+{
+ // only one thread can be in this method on any particular object
+ // at any given time. that's not a problem since only our event
+ // loop calls this method and there's just one of those.
+
+ ARCH->lockMutex(m_mutex);
+
+ // remove removed dialogs
+ m_dialogs.erase(false);
+
+ // merge added dialogs into the dialog list
+ for (Dialogs::const_iterator index = m_addedDialogs.begin();
+ index != m_addedDialogs.end(); ++index) {
+ m_dialogs.insert(std::make_pair(index->first, index->second));
+ }
+ m_addedDialogs.clear();
+
+ ARCH->unlockMutex(m_mutex);
+
+ // check message against all dialogs until one handles it.
+ // note that we don't hold a lock while checking because
+ // the message is processed and may make calls to this
+ // object. that's okay because addDialog() and
+ // removeDialog() don't change the map itself (just the
+ // values of some elements).
+ ARCH->lockMutex(m_mutex);
+ for (Dialogs::const_iterator index = m_dialogs.begin();
+ index != m_dialogs.end(); ++index) {
+ if (index->second) {
+ ARCH->unlockMutex(m_mutex);
+ if (IsDialogMessage(index->first, msg)) {
+ return true;
+ }
+ ARCH->lockMutex(m_mutex);
+ }
+ }
+ ARCH->unlockMutex(m_mutex);
+
+ return false;
+}
+
+LRESULT
+ArchTaskBarWindows::wndProc(HWND hwnd,
+ UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case kNotifyReceiver: {
+ // lookup receiver
+ CIDToReceiverMap::const_iterator index = m_idTable.find((UINT)wParam);
+ if (index != m_idTable.end()) {
+ IArchTaskBarReceiver* receiver = index->second->first;
+ handleIconMessage(receiver, lParam);
+ return 0;
+ }
+ break;
+ }
+
+ case kAddReceiver:
+ addIcon((UINT)wParam);
+ break;
+
+ case kRemoveReceiver:
+ removeIcon((UINT)wParam);
+ break;
+
+ case kUpdateReceiver:
+ updateIcon((UINT)wParam);
+ break;
+
+ default:
+ if (msg == m_taskBarRestart) {
+ // task bar was recreated so re-add our icons
+ addAllIcons();
+ }
+ break;
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+LRESULT CALLBACK
+ArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg,
+ WPARAM wParam, LPARAM lParam)
+{
+ // if msg is WM_NCCREATE, extract the ArchTaskBarWindows* and put
+ // it in the extra window data then forward the call.
+ ArchTaskBarWindows* self = NULL;
+ if (msg == WM_NCCREATE) {
+ CREATESTRUCT* createInfo;
+ createInfo = reinterpret_cast<CREATESTRUCT*>(lParam);
+ self = static_cast<ArchTaskBarWindows*>(
+ createInfo->lpCreateParams);
+ SetWindowLongPtr(hwnd, 0, reinterpret_cast<LONG_PTR>(createInfo->lpCreateParams));
+ }
+ else {
+ // get the extra window data and forward the call
+ LONG_PTR data = GetWindowLongPtr(hwnd, 0);
+ if (data != 0) {
+ self = static_cast<ArchTaskBarWindows*>(reinterpret_cast<void*>(data));
+ }
+ }
+
+ // forward the message
+ if (self != NULL) {
+ return self->wndProc(hwnd, msg, wParam, lParam);
+ }
+ else {
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+}
+
+void
+ArchTaskBarWindows::threadMainLoop()
+{
+ // register the task bar restart message
+ m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
+
+ // register a window class
+ LPCTSTR className = TEXT("BarrierTaskBar");
+ WNDCLASSEX classInfo;
+ classInfo.cbSize = sizeof(classInfo);
+ classInfo.style = CS_NOCLOSE;
+ classInfo.lpfnWndProc = &ArchTaskBarWindows::staticWndProc;
+ classInfo.cbClsExtra = 0;
+ classInfo.cbWndExtra = sizeof(ArchTaskBarWindows*);
+ classInfo.hInstance = instanceWin32();
+ classInfo.hIcon = NULL;
+ classInfo.hCursor = NULL;
+ classInfo.hbrBackground = NULL;
+ classInfo.lpszMenuName = NULL;
+ classInfo.lpszClassName = className;
+ classInfo.hIconSm = NULL;
+ ATOM windowClass = RegisterClassEx(&classInfo);
+
+ // create window
+ m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
+ className,
+ TEXT("Barrier Task Bar"),
+ WS_POPUP,
+ 0, 0, 1, 1,
+ NULL,
+ NULL,
+ instanceWin32(),
+ static_cast<void*>(this));
+
+ // signal ready
+ ARCH->lockMutex(m_mutex);
+ m_ready = true;
+ ARCH->broadcastCondVar(m_condVar);
+ ARCH->unlockMutex(m_mutex);
+
+ // handle failure
+ if (m_hwnd == NULL) {
+ UnregisterClass(className, instanceWin32());
+ return;
+ }
+
+ // main loop
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ if (!processDialogs(&msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ // clean up
+ removeAllIcons();
+ DestroyWindow(m_hwnd);
+ UnregisterClass(className, instanceWin32());
+}
+
+void*
+ArchTaskBarWindows::threadEntry(void* self)
+{
+ static_cast<ArchTaskBarWindows*>(self)->threadMainLoop();
+ return NULL;
+}
+
+HINSTANCE ArchTaskBarWindows::instanceWin32()
+{
+ return ArchMiscWindows::instanceWin32();
+} \ No newline at end of file
diff --git a/src/lib/arch/win32/ArchTaskBarWindows.h b/src/lib/arch/win32/ArchTaskBarWindows.h
new file mode 100644
index 0000000..0edddf8
--- /dev/null
+++ b/src/lib/arch/win32/ArchTaskBarWindows.h
@@ -0,0 +1,114 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "arch/IArchTaskBar.h"
+#include "arch/IArchMultithread.h"
+#include "common/stdmap.h"
+#include "common/stdvector.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#define ARCH_TASKBAR ArchTaskBarWindows
+
+//! Win32 implementation of IArchTaskBar
+class ArchTaskBarWindows : public IArchTaskBar {
+public:
+ ArchTaskBarWindows();
+ virtual ~ArchTaskBarWindows();
+
+ virtual void init();
+
+ //! Add a dialog window
+ /*!
+ Tell the task bar event loop about a dialog. Win32 annoyingly
+ requires messages destined for modeless dialog boxes to be
+ dispatched differently than other messages.
+ */
+ static void addDialog(HWND);
+
+ //! Remove a dialog window
+ /*!
+ Remove a dialog window added via \c addDialog().
+ */
+ static void removeDialog(HWND);
+
+ // IArchTaskBar overrides
+ virtual void addReceiver(IArchTaskBarReceiver*);
+ virtual void removeReceiver(IArchTaskBarReceiver*);
+ virtual void updateReceiver(IArchTaskBarReceiver*);
+
+private:
+ class ReceiverInfo {
+ public:
+ UINT m_id;
+ };
+
+ typedef std::map<IArchTaskBarReceiver*, ReceiverInfo> ReceiverToInfoMap;
+ typedef std::map<UINT, ReceiverToInfoMap::iterator> CIDToReceiverMap;
+ typedef std::vector<UINT> CIDStack;
+ typedef std::map<HWND, bool> Dialogs;
+
+ UINT getNextID();
+ void recycleID(UINT);
+
+ void addIcon(UINT);
+ void removeIcon(UINT);
+ void updateIcon(UINT);
+ void addAllIcons();
+ void removeAllIcons();
+ void modifyIconNoLock(ReceiverToInfoMap::const_iterator,
+ DWORD taskBarMessage);
+ void removeIconNoLock(UINT id);
+ void handleIconMessage(IArchTaskBarReceiver*, LPARAM);
+
+ bool processDialogs(MSG*);
+ LRESULT wndProc(HWND, UINT, WPARAM, LPARAM);
+ static LRESULT CALLBACK
+ staticWndProc(HWND, UINT, WPARAM, LPARAM);
+ void threadMainLoop();
+ static void* threadEntry(void*);
+
+ HINSTANCE instanceWin32();
+
+private:
+ static ArchTaskBarWindows* s_instance;
+
+ // multithread data
+ ArchMutex m_mutex;
+ ArchCond m_condVar;
+ bool m_ready;
+ int m_result;
+ ArchThread m_thread;
+
+ // child thread data
+ HWND m_hwnd;
+ UINT m_taskBarRestart;
+
+ // shared data
+ ReceiverToInfoMap m_receivers;
+ CIDToReceiverMap m_idTable;
+ CIDStack m_oldIDs;
+ UINT m_nextID;
+
+ // dialogs
+ Dialogs m_dialogs;
+ Dialogs m_addedDialogs;
+};
diff --git a/src/lib/arch/win32/ArchTimeWindows.cpp b/src/lib/arch/win32/ArchTimeWindows.cpp
new file mode 100644
index 0000000..568a483
--- /dev/null
+++ b/src/lib/arch/win32/ArchTimeWindows.cpp
@@ -0,0 +1,89 @@
+/*
+ * barrier -- 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 "arch/win32/ArchTimeWindows.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#define MMNODRV // Disable: Installable driver support
+#define MMNOSOUND // Disable: Sound support
+#define MMNOWAVE // Disable: Waveform support
+#define MMNOMIDI // Disable: MIDI support
+#define MMNOAUX // Disable: Auxiliary audio support
+#define MMNOMIXER // Disable: Mixer support
+#define MMNOJOY // Disable: Joystick support
+#define MMNOMCI // Disable: MCI support
+#define MMNOMMIO // Disable: Multimedia file I/O support
+#define MMNOMMSYSTEM // Disable: General MMSYSTEM functions
+#include <MMSystem.h>
+
+typedef WINMMAPI DWORD (WINAPI *PTimeGetTime)(void);
+
+static double s_freq = 0.0;
+static HINSTANCE s_mmInstance = NULL;
+static PTimeGetTime s_tgt = NULL;
+
+
+//
+// ArchTimeWindows
+//
+
+ArchTimeWindows::ArchTimeWindows()
+{
+ assert(s_freq == 0.0 || s_mmInstance == NULL);
+
+ LARGE_INTEGER freq;
+ if (QueryPerformanceFrequency(&freq) && freq.QuadPart != 0) {
+ s_freq = 1.0 / static_cast<double>(freq.QuadPart);
+ }
+ else {
+ // load winmm.dll and get timeGetTime
+ s_mmInstance = LoadLibrary("winmm");
+ if (s_mmInstance != NULL) {
+ s_tgt = (PTimeGetTime)GetProcAddress(s_mmInstance, "timeGetTime");
+ }
+ }
+}
+
+ArchTimeWindows::~ArchTimeWindows()
+{
+ s_freq = 0.0;
+ if (s_mmInstance == NULL) {
+ FreeLibrary(static_cast<HMODULE>(s_mmInstance));
+ s_tgt = NULL;
+ s_mmInstance = NULL;
+ }
+}
+
+double
+ArchTimeWindows::time()
+{
+ // get time. we try three ways, in order of descending precision
+ if (s_freq != 0.0) {
+ LARGE_INTEGER c;
+ QueryPerformanceCounter(&c);
+ return s_freq * static_cast<double>(c.QuadPart);
+ }
+ else if (s_tgt != NULL) {
+ return 0.001 * static_cast<double>(s_tgt());
+ }
+ else {
+ return 0.001 * static_cast<double>(GetTickCount());
+ }
+}
diff --git a/src/lib/arch/win32/ArchTimeWindows.h b/src/lib/arch/win32/ArchTimeWindows.h
new file mode 100644
index 0000000..42351a1
--- /dev/null
+++ b/src/lib/arch/win32/ArchTimeWindows.h
@@ -0,0 +1,33 @@
+/*
+ * barrier -- 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 "arch/IArchTime.h"
+
+#define ARCH_TIME ArchTimeWindows
+
+//! Win32 implementation of IArchTime
+class ArchTimeWindows : public IArchTime {
+public:
+ ArchTimeWindows();
+ virtual ~ArchTimeWindows();
+
+ // IArchTime overrides
+ virtual double time();
+};
diff --git a/src/lib/arch/win32/XArchWindows.cpp b/src/lib/arch/win32/XArchWindows.cpp
new file mode 100644
index 0000000..eeec0e1
--- /dev/null
+++ b/src/lib/arch/win32/XArchWindows.cpp
@@ -0,0 +1,120 @@
+/*
+ * barrier -- 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 "arch/win32/XArchWindows.h"
+#include "arch/win32/ArchNetworkWinsock.h"
+#include "base/String.h"
+
+//
+// XArchEvalWindows
+//
+
+std::string
+XArchEvalWindows::eval() const throw()
+{
+ char* cmsg;
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ 0,
+ m_error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&cmsg,
+ 0,
+ NULL) == 0) {
+ cmsg = NULL;
+ return barrier::string::sprintf("Unknown error, code %d", m_error);
+ }
+ std::string smsg(cmsg);
+ LocalFree(cmsg);
+ return smsg;
+}
+
+
+//
+// XArchEvalWinsock
+//
+
+std::string
+XArchEvalWinsock::eval() const throw()
+{
+ // built-in windows function for looking up error message strings
+ // may not look up network error messages correctly. we'll have
+ // to do it ourself.
+ static const struct { int m_code; const char* m_msg; } s_netErrorCodes[] = {
+ /* 10004 */{WSAEINTR, "The (blocking) call was canceled via WSACancelBlockingCall"},
+ /* 10009 */{WSAEBADF, "Bad file handle"},
+ /* 10013 */{WSAEACCES, "The requested address is a broadcast address, but the appropriate flag was not set"},
+ /* 10014 */{WSAEFAULT, "WSAEFAULT"},
+ /* 10022 */{WSAEINVAL, "WSAEINVAL"},
+ /* 10024 */{WSAEMFILE, "No more file descriptors available"},
+ /* 10035 */{WSAEWOULDBLOCK, "Socket is marked as non-blocking and no connections are present or the receive operation would block"},
+ /* 10036 */{WSAEINPROGRESS, "A blocking Windows Sockets operation is in progress"},
+ /* 10037 */{WSAEALREADY, "The asynchronous routine being canceled has already completed"},
+ /* 10038 */{WSAENOTSOCK, "At least on descriptor is not a socket"},
+ /* 10039 */{WSAEDESTADDRREQ, "A destination address is required"},
+ /* 10040 */{WSAEMSGSIZE, "The datagram was too large to fit into the specified buffer and was truncated"},
+ /* 10041 */{WSAEPROTOTYPE, "The specified protocol is the wrong type for this socket"},
+ /* 10042 */{WSAENOPROTOOPT, "The option is unknown or unsupported"},
+ /* 10043 */{WSAEPROTONOSUPPORT,"The specified protocol is not supported"},
+ /* 10044 */{WSAESOCKTNOSUPPORT,"The specified socket type is not supported by this address family"},
+ /* 10045 */{WSAEOPNOTSUPP, "The referenced socket is not a type that supports that operation"},
+ /* 10046 */{WSAEPFNOSUPPORT, "BSD: Protocol family not supported"},
+ /* 10047 */{WSAEAFNOSUPPORT, "The specified address family is not supported"},
+ /* 10048 */{WSAEADDRINUSE, "The specified address is already in use"},
+ /* 10049 */{WSAEADDRNOTAVAIL, "The specified address is not available from the local machine"},
+ /* 10050 */{WSAENETDOWN, "The Windows Sockets implementation has detected that the network subsystem has failed"},
+ /* 10051 */{WSAENETUNREACH, "The network can't be reached from this host at this time"},
+ /* 10052 */{WSAENETRESET, "The connection must be reset because the Windows Sockets implementation dropped it"},
+ /* 10053 */{WSAECONNABORTED, "The virtual circuit was aborted due to timeout or other failure"},
+ /* 10054 */{WSAECONNRESET, "The virtual circuit was reset by the remote side"},
+ /* 10055 */{WSAENOBUFS, "No buffer space is available or a buffer deadlock has occured. The socket cannot be created"},
+ /* 10056 */{WSAEISCONN, "The socket is already connected"},
+ /* 10057 */{WSAENOTCONN, "The socket is not connected"},
+ /* 10058 */{WSAESHUTDOWN, "The socket has been shutdown"},
+ /* 10059 */{WSAETOOMANYREFS, "BSD: Too many references"},
+ /* 10060 */{WSAETIMEDOUT, "Attempt to connect timed out without establishing a connection"},
+ /* 10061 */{WSAECONNREFUSED, "Connection was refused"},
+ /* 10062 */{WSAELOOP, "Undocumented WinSock error code used in BSD"},
+ /* 10063 */{WSAENAMETOOLONG, "Undocumented WinSock error code used in BSD"},
+ /* 10064 */{WSAEHOSTDOWN, "Undocumented WinSock error code used in BSD"},
+ /* 10065 */{WSAEHOSTUNREACH, "No route to host"},
+ /* 10066 */{WSAENOTEMPTY, "Undocumented WinSock error code"},
+ /* 10067 */{WSAEPROCLIM, "Undocumented WinSock error code"},
+ /* 10068 */{WSAEUSERS, "Undocumented WinSock error code"},
+ /* 10069 */{WSAEDQUOT, "Undocumented WinSock error code"},
+ /* 10070 */{WSAESTALE, "Undocumented WinSock error code"},
+ /* 10071 */{WSAEREMOTE, "Undocumented WinSock error code"},
+ /* 10091 */{WSASYSNOTREADY, "Underlying network subsytem is not ready for network communication"},
+ /* 10092 */{WSAVERNOTSUPPORTED, "The version of WinSock API support requested is not provided in this implementation"},
+ /* 10093 */{WSANOTINITIALISED, "WinSock subsystem not properly initialized"},
+ /* 10101 */{WSAEDISCON, "Virtual circuit has gracefully terminated connection"},
+ /* 11001 */{WSAHOST_NOT_FOUND, "The specified host is unknown"},
+ /* 11002 */{WSATRY_AGAIN, "A temporary error occurred on an authoritative name server"},
+ /* 11003 */{WSANO_RECOVERY, "A non-recoverable name server error occurred"},
+ /* 11004 */{WSANO_DATA, "The requested name is valid but does not have an IP address"},
+ /* end */{0, NULL}
+ };
+
+ for (unsigned int i = 0; s_netErrorCodes[i].m_code != 0; ++i) {
+ if (s_netErrorCodes[i].m_code == m_error) {
+ return s_netErrorCodes[i].m_msg;
+ }
+ }
+ return "Unknown error";
+}
diff --git a/src/lib/arch/win32/XArchWindows.h b/src/lib/arch/win32/XArchWindows.h
new file mode 100644
index 0000000..10106b1
--- /dev/null
+++ b/src/lib/arch/win32/XArchWindows.h
@@ -0,0 +1,49 @@
+/*
+ * barrier -- 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 "arch/XArch.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+//! Lazy error message string evaluation for windows
+class XArchEvalWindows : public XArchEval {
+public:
+ XArchEvalWindows() : m_error(GetLastError()) { }
+ XArchEvalWindows(DWORD error) : m_error(error) { }
+ virtual ~XArchEvalWindows() { }
+
+ virtual std::string eval() const;
+
+private:
+ DWORD m_error;
+};
+
+//! Lazy error message string evaluation for winsock
+class XArchEvalWinsock : public XArchEval {
+public:
+ XArchEvalWinsock(int error) : m_error(error) { }
+ virtual ~XArchEvalWinsock() { }
+
+ virtual std::string eval() const;
+
+private:
+ int m_error;
+};
diff --git a/src/lib/barrier/App.cpp b/src/lib/barrier/App.cpp
new file mode 100644
index 0000000..1f4eda3
--- /dev/null
+++ b/src/lib/barrier/App.cpp
@@ -0,0 +1,329 @@
+/*
+ * barrier -- 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 "barrier/App.h"
+
+#include "base/Log.h"
+#include "common/Version.h"
+#include "barrier/protocol_types.h"
+#include "arch/Arch.h"
+#include "base/XBase.h"
+#include "arch/XArch.h"
+#include "base/log_outputters.h"
+#include "barrier/XBarrier.h"
+#include "barrier/ArgsBase.h"
+#include "ipc/IpcServerProxy.h"
+#include "base/TMethodEventJob.h"
+#include "ipc/IpcMessage.h"
+#include "ipc/Ipc.h"
+#include "base/EventQueue.h"
+
+#if SYSAPI_WIN32
+#include "arch/win32/ArchMiscWindows.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodJob.h"
+#endif
+
+#include <iostream>
+#include <stdio.h>
+
+#if WINAPI_CARBON
+#include <ApplicationServices/ApplicationServices.h>
+#endif
+
+#if defined(__APPLE__)
+#include "platform/OSXDragSimulator.h"
+#endif
+
+App* App::s_instance = nullptr;
+
+//
+// App
+//
+
+App::App(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver, ArgsBase* args) :
+ m_bye(&exit),
+ m_taskBarReceiver(NULL),
+ m_suspended(false),
+ m_events(events),
+ m_args(args),
+ m_fileLog(nullptr),
+ m_createTaskBarReceiver(createTaskBarReceiver),
+ m_appUtil(events),
+ m_ipcClient(nullptr),
+ m_socketMultiplexer(nullptr)
+{
+ assert(s_instance == nullptr);
+ s_instance = this;
+}
+
+App::~App()
+{
+ s_instance = nullptr;
+ delete m_args;
+}
+
+void
+App::version()
+{
+ char buffer[500];
+ sprintf(
+ buffer,
+ "%s %s, protocol version %d.%d\n%s",
+ argsBase().m_pname,
+ kVersion,
+ kProtocolMajorVersion,
+ kProtocolMinorVersion,
+ kCopyright
+ );
+
+ std::cout << buffer << std::endl;
+}
+
+int
+App::run(int argc, char** argv)
+{
+#if MAC_OS_X_VERSION_10_7
+ // dock hide only supported on lion :(
+ ProcessSerialNumber psn = { 0, kCurrentProcess };
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ GetCurrentProcess(&psn);
+#pragma GCC diagnostic pop
+
+ TransformProcessType(&psn, kProcessTransformToBackgroundApplication);
+#endif
+
+ // install application in to arch
+ appUtil().adoptApp(this);
+
+ // HACK: fail by default (saves us setting result in each catch)
+ int result = kExitFailed;
+
+ try {
+ result = appUtil().run(argc, argv);
+ }
+ catch (XExitApp& e) {
+ // instead of showing a nasty error, just exit with the error code.
+ // not sure if i like this behaviour, but it's probably better than
+ // using the exit(int) function!
+ result = e.getCode();
+ }
+ catch (std::exception& e) {
+ LOG((CLOG_CRIT "An error occurred: %s\n", e.what()));
+ }
+ catch (...) {
+ LOG((CLOG_CRIT "An unknown error occurred.\n"));
+ }
+
+ appUtil().beforeAppExit();
+
+ return result;
+}
+
+int
+App::daemonMainLoop(int, const char**)
+{
+#if SYSAPI_WIN32
+ SystemLogger sysLogger(daemonName(), false);
+#else
+ SystemLogger sysLogger(daemonName(), true);
+#endif
+ return mainLoop();
+}
+
+void
+App::setupFileLogging()
+{
+ if (argsBase().m_logFile != NULL) {
+ m_fileLog = new FileLogOutputter(argsBase().m_logFile);
+ CLOG->insert(m_fileLog);
+ LOG((CLOG_DEBUG1 "logging to file (%s) enabled", argsBase().m_logFile));
+ }
+}
+
+void
+App::loggingFilterWarning()
+{
+ if (CLOG->getFilter() > CLOG->getConsoleMaxLevel()) {
+ if (argsBase().m_logFile == NULL) {
+ LOG((CLOG_WARN "log messages above %s are NOT sent to console (use file logging)",
+ CLOG->getFilterName(CLOG->getConsoleMaxLevel())));
+ }
+ }
+}
+
+void
+App::initApp(int argc, const char** argv)
+{
+ // parse command line
+ parseArgs(argc, argv);
+
+ ARCH->setProfileDirectory(argsBase().m_profileDirectory);
+ ARCH->setPluginDirectory(argsBase().m_pluginDirectory);
+
+ // set log filter
+ if (!CLOG->setFilter(argsBase().m_logFilter)) {
+ LOG((CLOG_PRINT "%s: unrecognized log level `%s'" BYE,
+ argsBase().m_pname, argsBase().m_logFilter, argsBase().m_pname));
+ m_bye(kExitArgs);
+ }
+ loggingFilterWarning();
+
+ if (argsBase().m_enableDragDrop) {
+ LOG((CLOG_INFO "drag and drop enabled"));
+ }
+
+ // setup file logging after parsing args
+ setupFileLogging();
+
+ // load configuration
+ loadConfig();
+
+ if (!argsBase().m_disableTray) {
+
+ // create a log buffer so we can show the latest message
+ // as a tray icon tooltip
+ BufferedLogOutputter* logBuffer = new BufferedLogOutputter(1000);
+ CLOG->insert(logBuffer, true);
+
+ // make the task bar receiver. the user can control this app
+ // through the task bar.
+ m_taskBarReceiver = m_createTaskBarReceiver(logBuffer, m_events);
+ }
+}
+
+void
+App::initIpcClient()
+{
+ m_ipcClient = new IpcClient(m_events, m_socketMultiplexer);
+ m_ipcClient->connect();
+
+ m_events->adoptHandler(
+ m_events->forIpcClient().messageReceived(), m_ipcClient,
+ new TMethodEventJob<App>(this, &App::handleIpcMessage));
+}
+
+void
+App::cleanupIpcClient()
+{
+ m_ipcClient->disconnect();
+ m_events->removeHandler(m_events->forIpcClient().messageReceived(), m_ipcClient);
+ delete m_ipcClient;
+}
+
+void
+App::handleIpcMessage(const Event& e, void*)
+{
+ IpcMessage* m = static_cast<IpcMessage*>(e.getDataObject());
+ if (m->type() == kIpcShutdown) {
+ LOG((CLOG_INFO "got ipc shutdown message"));
+ m_events->addEvent(Event(Event::kQuit));
+ }
+}
+
+void
+App::runEventsLoop(void*)
+{
+ m_events->loop();
+
+#if defined(MAC_OS_X_VERSION_10_7)
+
+ stopCocoaLoop();
+
+#endif
+}
+
+//
+// MinimalApp
+//
+
+MinimalApp::MinimalApp() :
+ App(NULL, NULL, new ArgsBase())
+{
+ m_arch.init();
+ setEvents(m_events);
+}
+
+MinimalApp::~MinimalApp()
+{
+}
+
+int
+MinimalApp::standardStartup(int argc, char** argv)
+{
+ return 0;
+}
+
+int
+MinimalApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
+{
+ return 0;
+}
+
+void
+MinimalApp::startNode()
+{
+}
+
+int
+MinimalApp::mainLoop()
+{
+ return 0;
+}
+
+int
+MinimalApp::foregroundStartup(int argc, char** argv)
+{
+ return 0;
+}
+
+barrier::Screen*
+MinimalApp::createScreen()
+{
+ return NULL;
+}
+
+void
+MinimalApp::loadConfig()
+{
+}
+
+bool
+MinimalApp::loadConfig(const String& pathname)
+{
+ return false;
+}
+
+const char*
+MinimalApp::daemonInfo() const
+{
+ return "";
+}
+
+const char*
+MinimalApp::daemonName() const
+{
+ return "";
+}
+
+void
+MinimalApp::parseArgs(int argc, const char* const* argv)
+{
+}
diff --git a/src/lib/barrier/App.h b/src/lib/barrier/App.h
new file mode 100644
index 0000000..b7c77a0
--- /dev/null
+++ b/src/lib/barrier/App.h
@@ -0,0 +1,200 @@
+/*
+ * barrier -- 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 "ipc/IpcClient.h"
+#include "barrier/IApp.h"
+#include "base/String.h"
+#include "base/Log.h"
+#include "base/EventQueue.h"
+#include "common/common.h"
+
+#if SYSAPI_WIN32
+#include "barrier/win32/AppUtilWindows.h"
+#elif SYSAPI_UNIX
+#include "barrier/unix/AppUtilUnix.h"
+#endif
+
+class IArchTaskBarReceiver;
+class BufferedLogOutputter;
+class ILogOutputter;
+class FileLogOutputter;
+namespace barrier { class Screen; }
+class IEventQueue;
+class SocketMultiplexer;
+
+typedef IArchTaskBarReceiver* (*CreateTaskBarReceiverFunc)(const BufferedLogOutputter*, IEventQueue* events);
+
+class App : public IApp {
+public:
+ App(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver, ArgsBase* args);
+ virtual ~App();
+
+ // Returns args that are common between server and client.
+ ArgsBase& argsBase() const { return *m_args; }
+
+ // Prints the current compiled version.
+ virtual void version();
+
+ // Prints help specific to client or server.
+ virtual void help() = 0;
+
+ // Parse command line arguments.
+ virtual void parseArgs(int argc, const char* const* argv) = 0;
+
+ int run(int argc, char** argv);
+
+ int daemonMainLoop(int, const char**);
+
+ virtual void loadConfig() = 0;
+ virtual bool loadConfig(const String& pathname) = 0;
+
+ // A description of the daemon (used only on Windows).
+ virtual const char* daemonInfo() const = 0;
+
+ // Function pointer for function to exit immediately.
+ // TODO: this is old C code - use inheritance to normalize
+ void (*m_bye)(int);
+
+ static App& instance() { assert(s_instance != nullptr); return *s_instance; }
+
+ // If --log was specified in args, then add a file logger.
+ void setupFileLogging();
+
+ // If messages will be hidden (to improve performance), warn user.
+ void loggingFilterWarning();
+
+ // Parses args, sets up file logging, and loads the config.
+ void initApp(int argc, const char** argv);
+
+ // HACK: accept non-const, but make it const anyway
+ void initApp(int argc, char** argv) { initApp(argc, (const char**)argv); }
+
+ ARCH_APP_UTIL& appUtil() { return m_appUtil; }
+
+ virtual IArchTaskBarReceiver* taskBarReceiver() const { return m_taskBarReceiver; }
+
+ virtual void setByeFunc(void(*bye)(int)) { m_bye = bye; }
+ virtual void bye(int error) { m_bye(error); }
+
+ virtual IEventQueue* getEvents() const { return m_events; }
+
+ void setSocketMultiplexer(SocketMultiplexer* sm) { m_socketMultiplexer = sm; }
+ SocketMultiplexer* getSocketMultiplexer() const { return m_socketMultiplexer; }
+
+ void setEvents(EventQueue& events) { m_events = &events; }
+
+private:
+ void handleIpcMessage(const Event&, void*);
+
+protected:
+ void initIpcClient();
+ void cleanupIpcClient();
+ void runEventsLoop(void*);
+
+ IArchTaskBarReceiver* m_taskBarReceiver;
+ bool m_suspended;
+ IEventQueue* m_events;
+
+private:
+ ArgsBase* m_args;
+ static App* s_instance;
+ FileLogOutputter* m_fileLog;
+ CreateTaskBarReceiverFunc m_createTaskBarReceiver;
+ ARCH_APP_UTIL m_appUtil;
+ IpcClient* m_ipcClient;
+ SocketMultiplexer* m_socketMultiplexer;
+};
+
+class MinimalApp : public App {
+public:
+ MinimalApp();
+ virtual ~MinimalApp();
+
+ // IApp overrides
+ virtual int standardStartup(int argc, char** argv);
+ virtual int runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup);
+ virtual void startNode();
+ virtual int mainLoop();
+ virtual int foregroundStartup(int argc, char** argv);
+ virtual barrier::Screen*
+ createScreen();
+ virtual void loadConfig();
+ virtual bool loadConfig(const String& pathname);
+ virtual const char* daemonInfo() const;
+ virtual const char* daemonName() const;
+ virtual void parseArgs(int argc, const char* const* argv);
+
+private:
+ Arch m_arch;
+ Log m_log;
+ EventQueue m_events;
+};
+
+#if WINAPI_MSWINDOWS
+#define DAEMON_RUNNING(running_) ArchMiscWindows::daemonRunning(running_)
+#else
+#define DAEMON_RUNNING(running_)
+#endif
+
+#define HELP_COMMON_INFO_1 \
+ " -d, --debug <level> filter out log messages with priority below level.\n" \
+ " level may be: FATAL, ERROR, WARNING, NOTE, INFO,\n" \
+ " DEBUG, DEBUG1, DEBUG2.\n" \
+ " -n, --name <screen-name> use screen-name instead the hostname to identify\n" \
+ " this screen in the configuration.\n" \
+ " -1, --no-restart do not try to restart on failure.\n" \
+ " --restart restart the server automatically if it fails. (*)\n" \
+ " -l --log <file> write log messages to file.\n" \
+ " --no-tray disable the system tray icon.\n" \
+ " --enable-drag-drop enable file drag & drop.\n" \
+ " --enable-crypto enable the crypto (ssl) plugin.\n"
+
+#define HELP_COMMON_INFO_2 \
+ " -h, --help display this help and exit.\n" \
+ " --version display version information and exit.\n"
+
+#define HELP_COMMON_ARGS \
+ " [--name <screen-name>]" \
+ " [--restart|--no-restart]" \
+ " [--debug <level>]"
+
+// system args (windows/unix)
+#if SYSAPI_UNIX
+
+// unix daemon mode args
+# define HELP_SYS_ARGS \
+ " [--daemon|--no-daemon]"
+# define HELP_SYS_INFO \
+ " -f, --no-daemon run in the foreground.\n" \
+ " --daemon run as a daemon. (*)\n"
+
+#elif SYSAPI_WIN32
+
+// windows args
+# define HELP_SYS_ARGS \
+ " [--service <action>] [--relaunch] [--exit-pause]"
+# define HELP_SYS_INFO \
+ " --service <action> manage the windows service, valid options are:\n" \
+ " install/uninstall/start/stop\n" \
+ " --relaunch persistently relaunches process in current user \n" \
+ " session (useful for vista and upward).\n" \
+ " --exit-pause wait for key press on exit, can be useful for\n" \
+ " reading error messages that occur on exit.\n"
+#endif
diff --git a/src/lib/barrier/AppUtil.cpp b/src/lib/barrier/AppUtil.cpp
new file mode 100644
index 0000000..3298d7b
--- /dev/null
+++ b/src/lib/barrier/AppUtil.cpp
@@ -0,0 +1,52 @@
+/*
+ * barrier -- 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 "barrier/AppUtil.h"
+
+AppUtil* AppUtil::s_instance = nullptr;
+
+AppUtil::AppUtil() :
+m_app(nullptr)
+{
+ s_instance = this;
+}
+
+AppUtil::~AppUtil()
+{
+}
+
+void
+AppUtil::adoptApp(IApp* app)
+{
+ app->setByeFunc(&exitAppStatic);
+ m_app = app;
+}
+
+IApp&
+AppUtil::app() const
+{
+ assert(m_app != nullptr);
+ return *m_app;
+}
+
+AppUtil&
+AppUtil::instance()
+{
+ assert(s_instance != nullptr);
+ return *s_instance;
+}
diff --git a/src/lib/barrier/AppUtil.h b/src/lib/barrier/AppUtil.h
new file mode 100644
index 0000000..6f5f073
--- /dev/null
+++ b/src/lib/barrier/AppUtil.h
@@ -0,0 +1,40 @@
+/*
+ * barrier -- 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 "barrier/IAppUtil.h"
+#include "barrier/XBarrier.h"
+
+class AppUtil : public IAppUtil {
+public:
+ AppUtil();
+ virtual ~AppUtil();
+
+ virtual void adoptApp(IApp* app);
+ IApp& app() const;
+ virtual void exitApp(int code) { throw XExitApp(code); }
+
+ static AppUtil& instance();
+ static void exitAppStatic(int code) { instance().exitApp(code); }
+ virtual void beforeAppExit() {}
+
+private:
+ IApp* m_app;
+ static AppUtil* s_instance;
+};
diff --git a/src/lib/barrier/ArgParser.cpp b/src/lib/barrier/ArgParser.cpp
new file mode 100644
index 0000000..20e849c
--- /dev/null
+++ b/src/lib/barrier/ArgParser.cpp
@@ -0,0 +1,519 @@
+/*
+ * barrier -- 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 "barrier/ArgParser.h"
+
+#include "barrier/StreamChunker.h"
+#include "barrier/App.h"
+#include "barrier/ServerArgs.h"
+#include "barrier/ClientArgs.h"
+#include "barrier/ToolArgs.h"
+#include "barrier/ArgsBase.h"
+#include "base/Log.h"
+#include "base/String.h"
+
+#ifdef WINAPI_MSWINDOWS
+#include <VersionHelpers.h>
+#endif
+
+ArgsBase* ArgParser::m_argsBase = NULL;
+
+ArgParser::ArgParser(App* app) :
+ m_app(app)
+{
+}
+
+bool
+ArgParser::parseServerArgs(ServerArgs& args, int argc, const char* const* argv)
+{
+ setArgsBase(args);
+ updateCommonArgs(argv);
+
+ for (int i = 1; i < argc; ++i) {
+ if (parsePlatformArg(args, argc, argv, i)) {
+ continue;
+ }
+ else if (parseGenericArgs(argc, argv, i)) {
+ continue;
+ }
+ else if (parseDeprecatedArgs(argc, argv, i)) {
+ continue;
+ }
+ else if (isArg(i, argc, argv, "-a", "--address", 1)) {
+ // save listen address
+ args.m_barrierAddress = argv[++i];
+ }
+ else if (isArg(i, argc, argv, "-c", "--config", 1)) {
+ // save configuration file path
+ args.m_configFile = argv[++i];
+ }
+ else {
+ LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, args.m_pname, argv[i], args.m_pname));
+ return false;
+ }
+ }
+
+ if (checkUnexpectedArgs()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ArgParser::parseClientArgs(ClientArgs& args, int argc, const char* const* argv)
+{
+ setArgsBase(args);
+ updateCommonArgs(argv);
+
+ int i;
+ for (i = 1; i < argc; ++i) {
+ if (parsePlatformArg(args, argc, argv, i)) {
+ continue;
+ }
+ else if (parseGenericArgs(argc, argv, i)) {
+ continue;
+ }
+ else if (parseDeprecatedArgs(argc, argv, i)) {
+ continue;
+ }
+ else if (isArg(i, argc, argv, NULL, "--camp")) {
+ // ignore -- included for backwards compatibility
+ }
+ else if (isArg(i, argc, argv, NULL, "--no-camp")) {
+ // ignore -- included for backwards compatibility
+ }
+ else if (isArg(i, argc, argv, NULL, "--yscroll", 1)) {
+ // define scroll
+ args.m_yscroll = atoi(argv[++i]);
+ }
+ else {
+ if (i + 1 == argc) {
+ args.m_barrierAddress = argv[i];
+ return true;
+ }
+
+ LOG((CLOG_PRINT "%s: unrecognized option `%s'" BYE, args.m_pname, argv[i], args.m_pname));
+ return false;
+ }
+ }
+
+ if (args.m_shouldExit)
+ return true;
+
+ // exactly one non-option argument (server-address)
+ if (i == argc) {
+ LOG((CLOG_PRINT "%s: a server address or name is required" BYE,
+ args.m_pname, args.m_pname));
+ return false;
+ }
+
+ if (checkUnexpectedArgs()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ArgParser::parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i)
+{
+#if WINAPI_MSWINDOWS
+ if (isArg(i, argc, argv, NULL, "--service")) {
+ LOG((CLOG_WARN "obsolete argument --service, use barrierd instead."));
+ argsBase.m_shouldExit = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--exit-pause")) {
+ argsBase.m_pauseOnExit = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--stop-on-desk-switch")) {
+ argsBase.m_stopOnDeskSwitch = true;
+ }
+ else {
+ // option not supported here
+ return false;
+ }
+
+ return true;
+#elif WINAPI_XWINDOWS
+ if (isArg(i, argc, argv, "-display", "--display", 1)) {
+ // use alternative display
+ argsBase.m_display = argv[++i];
+ }
+
+ else if (isArg(i, argc, argv, NULL, "--no-xinitthreads")) {
+ argsBase.m_disableXInitThreads = true;
+ }
+
+ else {
+ // option not supported here
+ return false;
+ }
+
+ return true;
+#elif WINAPI_CARBON
+ // no options for carbon
+ return false;
+#endif
+}
+
+bool
+ArgParser::parseToolArgs(ToolArgs& args, int argc, const char* const* argv)
+{
+ for (int i = 1; i < argc; ++i) {
+ if (isArg(i, argc, argv, NULL, "--get-active-desktop", 0)) {
+ args.m_printActiveDesktopName = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--login-auth", 0)) {
+ args.m_loginAuthenticate = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--get-installed-dir", 0)) {
+ args.m_getInstalledDir = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--get-profile-dir", 0)) {
+ args.m_getProfileDir = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--get-arch", 0)) {
+ args.m_getArch = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--notify-activation", 0)) {
+ args.m_notifyActivation = true;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--notify-update", 0)) {
+ args.m_notifyUpdate = true;
+ return true;
+ }
+ else {
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool
+ArgParser::parseGenericArgs(int argc, const char* const* argv, int& i)
+{
+ if (isArg(i, argc, argv, "-d", "--debug", 1)) {
+ // change logging level
+ argsBase().m_logFilter = argv[++i];
+ }
+ else if (isArg(i, argc, argv, "-l", "--log", 1)) {
+ argsBase().m_logFile = argv[++i];
+ }
+ else if (isArg(i, argc, argv, "-f", "--no-daemon")) {
+ // not a daemon
+ argsBase().m_daemon = false;
+ }
+ else if (isArg(i, argc, argv, NULL, "--daemon")) {
+ // daemonize
+ argsBase().m_daemon = true;
+ }
+ else if (isArg(i, argc, argv, "-n", "--name", 1)) {
+ // save screen name
+ argsBase().m_name = argv[++i];
+ }
+ else if (isArg(i, argc, argv, "-1", "--no-restart")) {
+ // don't try to restart
+ argsBase().m_restartable = false;
+ }
+ else if (isArg(i, argc, argv, NULL, "--restart")) {
+ // try to restart
+ argsBase().m_restartable = true;
+ }
+ else if (isArg(i, argc, argv, "-z", NULL)) {
+ argsBase().m_backend = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--no-hooks")) {
+ argsBase().m_noHooks = true;
+ }
+ else if (isArg(i, argc, argv, "-h", "--help")) {
+ if (m_app) {
+ m_app->help();
+ }
+ argsBase().m_shouldExit = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--version")) {
+ if (m_app) {
+ m_app->version();
+ }
+ argsBase().m_shouldExit = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--no-tray")) {
+ argsBase().m_disableTray = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--ipc")) {
+ argsBase().m_enableIpc = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--server")) {
+ // HACK: stop error happening when using portable (barrierp)
+ }
+ else if (isArg(i, argc, argv, NULL, "--client")) {
+ // HACK: stop error happening when using portable (barrierp)
+ }
+ else if (isArg(i, argc, argv, NULL, "--enable-drag-drop")) {
+ bool useDragDrop = true;
+
+#ifdef WINAPI_XWINDOWS
+
+ useDragDrop = false;
+ LOG((CLOG_INFO "ignoring --enable-drag-drop, not supported on linux."));
+
+#endif
+
+#ifdef WINAPI_MSWINDOWS
+
+ if (!IsWindowsVistaOrGreater()) {
+ useDragDrop = false;
+ LOG((CLOG_INFO "ignoring --enable-drag-drop, not supported below vista."));
+ }
+#endif
+
+ if (useDragDrop) {
+ argsBase().m_enableDragDrop = true;
+ }
+ }
+ else if (isArg(i, argc, argv, NULL, "--enable-crypto")) {
+ argsBase().m_enableCrypto = true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--profile-dir", 1)) {
+ argsBase().m_profileDirectory = argv[++i];
+ }
+ else if (isArg(i, argc, argv, NULL, "--plugin-dir", 1)) {
+ argsBase().m_pluginDirectory = argv[++i];
+ }
+ else {
+ // option not supported here
+ return false;
+ }
+
+ return true;
+}
+
+bool
+ArgParser::parseDeprecatedArgs(int argc, const char* const* argv, int& i)
+{
+ if (isArg(i, argc, argv, NULL, "--crypto-pass")) {
+ LOG((CLOG_NOTE "--crypto-pass is deprecated"));
+ i++;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--res-w")) {
+ LOG((CLOG_NOTE "--res-w is deprecated"));
+ i++;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--res-h")) {
+ LOG((CLOG_NOTE "--res-h is deprecated"));
+ i++;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--prm-wc")) {
+ LOG((CLOG_NOTE "--prm-wc is deprecated"));
+ i++;
+ return true;
+ }
+ else if (isArg(i, argc, argv, NULL, "--prm-hc")) {
+ LOG((CLOG_NOTE "--prm-hc is deprecated"));
+ i++;
+ return true;
+ }
+
+ return false;
+}
+
+bool
+ArgParser::isArg(
+ int argi, int argc, const char* const* argv,
+ const char* name1, const char* name2,
+ int minRequiredParameters)
+{
+ if ((name1 != NULL && strcmp(argv[argi], name1) == 0) ||
+ (name2 != NULL && strcmp(argv[argi], name2) == 0)) {
+ // match. check args left.
+ if (argi + minRequiredParameters >= argc) {
+ LOG((CLOG_PRINT "%s: missing arguments for `%s'" BYE,
+ argsBase().m_pname, argv[argi], argsBase().m_pname));
+ argsBase().m_shouldExit = true;
+ return false;
+ }
+ return true;
+ }
+
+ // no match
+ return false;
+}
+
+void
+ArgParser::splitCommandString(String& command, std::vector<String>& argv)
+{
+ if (command.empty()) {
+ return ;
+ }
+
+ size_t leftDoubleQuote = 0;
+ size_t rightDoubleQuote = 0;
+ searchDoubleQuotes(command, leftDoubleQuote, rightDoubleQuote);
+
+ size_t startPos = 0;
+ size_t space = command.find(" ", startPos);
+
+ while (space != String::npos) {
+ bool ignoreThisSpace = false;
+
+ // check if the space is between two double quotes
+ if (space > leftDoubleQuote && space < rightDoubleQuote) {
+ ignoreThisSpace = true;
+ }
+ else if (space > rightDoubleQuote){
+ searchDoubleQuotes(command, leftDoubleQuote, rightDoubleQuote, rightDoubleQuote + 1);
+ }
+
+ if (!ignoreThisSpace) {
+ String subString = command.substr(startPos, space - startPos);
+
+ removeDoubleQuotes(subString);
+ argv.push_back(subString);
+ }
+
+ // find next space
+ if (ignoreThisSpace) {
+ space = command.find(" ", rightDoubleQuote + 1);
+ }
+ else {
+ startPos = space + 1;
+ space = command.find(" ", startPos);
+ }
+ }
+
+ String subString = command.substr(startPos, command.size());
+ removeDoubleQuotes(subString);
+ argv.push_back(subString);
+}
+
+bool
+ArgParser::searchDoubleQuotes(String& command, size_t& left, size_t& right, size_t startPos)
+{
+ bool result = false;
+ left = String::npos;
+ right = String::npos;
+
+ left = command.find("\"", startPos);
+ if (left != String::npos) {
+ right = command.find("\"", left + 1);
+ if (right != String::npos) {
+ result = true;
+ }
+ }
+
+ if (!result) {
+ left = 0;
+ right = 0;
+ }
+
+ return result;
+}
+
+void
+ArgParser::removeDoubleQuotes(String& arg)
+{
+ // if string is surrounded by double quotes, remove them
+ if (arg[0] == '\"' &&
+ arg[arg.size() - 1] == '\"') {
+ arg = arg.substr(1, arg.size() - 2);
+ }
+}
+
+const char**
+ArgParser::getArgv(std::vector<String>& argsArray)
+{
+ size_t argc = argsArray.size();
+
+ // caller is responsible for deleting the outer array only
+ // we use the c string pointers from argsArray and assign
+ // them to the inner array. So caller only need to use
+ // delete[] to delete the outer array
+ const char** argv = new const char*[argc];
+
+ for (size_t i = 0; i < argc; i++) {
+ argv[i] = argsArray[i].c_str();
+ }
+
+ return argv;
+}
+
+String
+ArgParser::assembleCommand(std::vector<String>& argsArray, String ignoreArg, int parametersRequired)
+{
+ String result;
+
+ for (std::vector<String>::iterator it = argsArray.begin(); it != argsArray.end(); ++it) {
+ if (it->compare(ignoreArg) == 0) {
+ it = it + parametersRequired;
+ continue;
+ }
+
+ // if there is a space in this arg, use double quotes surround it
+ if ((*it).find(" ") != String::npos) {
+ (*it).insert(0, "\"");
+ (*it).push_back('\"');
+ }
+
+ result.append(*it);
+ // add space to saperate args
+ result.append(" ");
+ }
+
+ if (!result.empty()) {
+ // remove the tail space
+ result = result.substr(0, result.size() - 1);
+ }
+
+ return result;
+}
+
+void
+ArgParser::updateCommonArgs(const char* const* argv)
+{
+ argsBase().m_name = ARCH->getHostName();
+ argsBase().m_pname = ARCH->getBasename(argv[0]);
+}
+
+bool
+ArgParser::checkUnexpectedArgs()
+{
+#if SYSAPI_WIN32
+ // suggest that user installs as a windows service. when launched as
+ // service, process should automatically detect that it should run in
+ // daemon mode.
+ if (argsBase().m_daemon) {
+ LOG((CLOG_ERR
+ "the --daemon argument is not supported on windows. "
+ "instead, install %s as a service (--service install)",
+ argsBase().m_pname));
+ return true;
+ }
+#endif
+
+ return false;
+}
diff --git a/src/lib/barrier/ArgParser.h b/src/lib/barrier/ArgParser.h
new file mode 100644
index 0000000..5fc2649
--- /dev/null
+++ b/src/lib/barrier/ArgParser.h
@@ -0,0 +1,63 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include "base/String.h"
+#include "common/stdvector.h"
+
+class ServerArgs;
+class ClientArgs;
+class ToolArgs;
+class ArgsBase;
+class App;
+
+class ArgParser {
+
+public:
+ ArgParser(App* app);
+
+ bool parseServerArgs(ServerArgs& args, int argc, const char* const* argv);
+ bool parseClientArgs(ClientArgs& args, int argc, const char* const* argv);
+ bool parsePlatformArg(ArgsBase& argsBase, const int& argc, const char* const* argv, int& i);
+ bool parseToolArgs(ToolArgs& args, int argc, const char* const* argv);
+ bool parseGenericArgs(int argc, const char* const* argv, int& i);
+ bool parseDeprecatedArgs(int argc, const char* const* argv, int& i);
+ void setArgsBase(ArgsBase& argsBase) { m_argsBase = &argsBase; }
+
+ static bool isArg(int argi, int argc, const char* const* argv,
+ const char* name1, const char* name2,
+ int minRequiredParameters = 0);
+ static void splitCommandString(String& command, std::vector<String>& argv);
+ static bool searchDoubleQuotes(String& command, size_t& left,
+ size_t& right, size_t startPos = 0);
+ static void removeDoubleQuotes(String& arg);
+ static const char** getArgv(std::vector<String>& argsArray);
+ static String assembleCommand(std::vector<String>& argsArray,
+ String ignoreArg = "", int parametersRequired = 0);
+
+private:
+ void updateCommonArgs(const char* const* argv);
+ bool checkUnexpectedArgs();
+
+ static ArgsBase& argsBase() { return *m_argsBase; }
+
+private:
+ App* m_app;
+
+ static ArgsBase* m_argsBase;
+};
diff --git a/src/lib/barrier/ArgsBase.cpp b/src/lib/barrier/ArgsBase.cpp
new file mode 100644
index 0000000..eedb312
--- /dev/null
+++ b/src/lib/barrier/ArgsBase.cpp
@@ -0,0 +1,53 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "barrier/ArgsBase.h"
+
+ArgsBase::ArgsBase() :
+#if SYSAPI_WIN32
+m_daemon(false), // daemon mode not supported on windows (use --service)
+m_debugServiceWait(false),
+m_pauseOnExit(false),
+m_stopOnDeskSwitch(false),
+#else
+m_daemon(true), // backward compatibility for unix (daemon by default)
+#endif
+#if WINAPI_XWINDOWS
+m_disableXInitThreads(false),
+#endif
+m_backend(false),
+m_restartable(true),
+m_noHooks(false),
+m_pname(NULL),
+m_logFilter(NULL),
+m_logFile(NULL),
+m_display(NULL),
+m_disableTray(false),
+m_enableIpc(false),
+m_enableDragDrop(false),
+m_shouldExit(false),
+m_barrierAddress(),
+m_enableCrypto(false),
+m_profileDirectory(""),
+m_pluginDirectory("")
+{
+}
+
+ArgsBase::~ArgsBase()
+{
+}
diff --git a/src/lib/barrier/ArgsBase.h b/src/lib/barrier/ArgsBase.h
new file mode 100644
index 0000000..1f49984
--- /dev/null
+++ b/src/lib/barrier/ArgsBase.h
@@ -0,0 +1,54 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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"
+
+class ArgsBase {
+public:
+ ArgsBase();
+ virtual ~ArgsBase();
+
+public:
+ bool m_daemon;
+ bool m_backend;
+ bool m_restartable;
+ bool m_noHooks;
+ const char* m_pname;
+ const char* m_logFilter;
+ const char* m_logFile;
+ const char* m_display;
+ String m_name;
+ bool m_disableTray;
+ bool m_enableIpc;
+ bool m_enableDragDrop;
+#if SYSAPI_WIN32
+ bool m_debugServiceWait;
+ bool m_pauseOnExit;
+ bool m_stopOnDeskSwitch;
+#endif
+#if WINAPI_XWINDOWS
+ bool m_disableXInitThreads;
+#endif
+ bool m_shouldExit;
+ String m_barrierAddress;
+ bool m_enableCrypto;
+ String m_profileDirectory;
+ String m_pluginDirectory;
+};
diff --git a/src/lib/barrier/CMakeLists.txt b/src/lib/barrier/CMakeLists.txt
new file mode 100644
index 0000000..6978aef
--- /dev/null
+++ b/src/lib/barrier/CMakeLists.txt
@@ -0,0 +1,40 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+# arch
+if (WIN32)
+ file(GLOB arch_headers "win32/*.h")
+ file(GLOB arch_sources "win32/*.cpp")
+elseif (UNIX)
+ file(GLOB arch_headers "unix/*.h")
+ file(GLOB arch_sources "unix/*.cpp")
+endif()
+
+list(APPEND sources ${arch_sources})
+list(APPEND headers ${arch_headers})
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(synlib STATIC ${sources})
+
+if (UNIX)
+ target_link_libraries(synlib arch client ipc net base platform mt server)
+endif()
diff --git a/src/lib/barrier/Chunk.cpp b/src/lib/barrier/Chunk.cpp
new file mode 100644
index 0000000..f11bff5
--- /dev/null
+++ b/src/lib/barrier/Chunk.cpp
@@ -0,0 +1,30 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "barrier/Chunk.h"
+#include "base/String.h"
+
+Chunk::Chunk(size_t size): m_dataSize(0)
+{
+ m_chunk = new char[size];
+ memset(m_chunk, 0, size);
+}
+
+Chunk::~Chunk()
+{
+ delete[] m_chunk;
+}
diff --git a/src/lib/barrier/Chunk.h b/src/lib/barrier/Chunk.h
new file mode 100644
index 0000000..42b85bf
--- /dev/null
+++ b/src/lib/barrier/Chunk.h
@@ -0,0 +1,30 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+#include "common/basic_types.h"
+
+class Chunk {
+public:
+ Chunk(size_t size);
+ ~Chunk();
+
+public:
+ size_t m_dataSize;
+ char* m_chunk;
+};
diff --git a/src/lib/barrier/ClientApp.cpp b/src/lib/barrier/ClientApp.cpp
new file mode 100644
index 0000000..87686f2
--- /dev/null
+++ b/src/lib/barrier/ClientApp.cpp
@@ -0,0 +1,560 @@
+/*
+ * barrier -- 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 "barrier/ClientApp.h"
+
+#include "client/Client.h"
+#include "barrier/ArgParser.h"
+#include "barrier/protocol_types.h"
+#include "barrier/Screen.h"
+#include "barrier/XScreen.h"
+#include "barrier/ClientArgs.h"
+#include "net/NetworkAddress.h"
+#include "net/TCPSocketFactory.h"
+#include "net/SocketMultiplexer.h"
+#include "net/XSocket.h"
+#include "mt/Thread.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "arch/Arch.h"
+#include "base/String.h"
+#include "base/Event.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+#include "base/log_outputters.h"
+#include "base/EventQueue.h"
+#include "base/TMethodJob.h"
+#include "base/Log.h"
+#include "common/Version.h"
+
+#if SYSAPI_WIN32
+#include "arch/win32/ArchMiscWindows.h"
+#endif
+
+#if WINAPI_MSWINDOWS
+#include "platform/MSWindowsScreen.h"
+#elif WINAPI_XWINDOWS
+#include "platform/XWindowsScreen.h"
+#elif WINAPI_CARBON
+#include "platform/OSXScreen.h"
+#endif
+
+#if defined(__APPLE__)
+#include "platform/OSXDragSimulator.h"
+#endif
+
+#include <iostream>
+#include <stdio.h>
+#include <sstream>
+
+#define RETRY_TIME 1.0
+
+ClientApp::ClientApp(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver) :
+ App(events, createTaskBarReceiver, new ClientArgs()),
+ m_client(NULL),
+ m_clientScreen(NULL),
+ m_serverAddress(NULL)
+{
+}
+
+ClientApp::~ClientApp()
+{
+}
+
+void
+ClientApp::parseArgs(int argc, const char* const* argv)
+{
+ ArgParser argParser(this);
+ bool result = argParser.parseClientArgs(args(), argc, argv);
+
+ if (!result || args().m_shouldExit) {
+ m_bye(kExitArgs);
+ }
+ else {
+ // save server address
+ if (!args().m_barrierAddress.empty()) {
+ try {
+ *m_serverAddress = NetworkAddress(args().m_barrierAddress, kDefaultPort);
+ m_serverAddress->resolve();
+ }
+ catch (XSocketAddress& e) {
+ // allow an address that we can't look up if we're restartable.
+ // we'll try to resolve the address each time we connect to the
+ // server. a bad port will never get better. patch by Brent
+ // Priddy.
+ if (!args().m_restartable || e.getError() == XSocketAddress::kBadPort) {
+ LOG((CLOG_PRINT "%s: %s" BYE,
+ args().m_pname, e.what(), args().m_pname));
+ m_bye(kExitFailed);
+ }
+ }
+ }
+ }
+}
+
+void
+ClientApp::help()
+{
+#if WINAPI_XWINDOWS
+# define WINAPI_ARG \
+ " [--display <display>] [--no-xinitthreads]"
+# define WINAPI_INFO \
+ " --display <display> connect to the X server at <display>\n" \
+ " --no-xinitthreads do not call XInitThreads()\n"
+#else
+# define WINAPI_ARG ""
+# define WINAPI_INFO ""
+#endif
+
+ std::ostringstream buffer;
+ buffer << "Start the barrier client and connect to a remote server component." << std::endl
+ << std::endl
+ << "Usage: " << args().m_pname << " [--yscroll <delta>]" << WINAPI_ARG << HELP_SYS_ARGS
+ << HELP_COMMON_ARGS << " <server-address>" << std::endl
+ << std::endl
+ << "Options:" << std::endl
+ << HELP_COMMON_INFO_1 << WINAPI_INFO << HELP_SYS_INFO
+ << " --yscroll <delta> defines the vertical scrolling delta, which is" << std::endl
+ << " 120 by default." << std::endl
+ << HELP_COMMON_INFO_2
+ << std::endl
+ << "Default options are marked with a *" << std::endl
+ << std::endl
+ << "The server address is of the form: [<hostname>][:<port>]. The hostname" << std::endl
+ << "must be the address or hostname of the server. The port overrides the" << std::endl
+ << "default port, " << kDefaultPort << "." << std::endl;
+
+ LOG((CLOG_PRINT "%s", buffer.str().c_str()));
+}
+
+const char*
+ClientApp::daemonName() const
+{
+#if SYSAPI_WIN32
+ return "Barrier Client";
+#elif SYSAPI_UNIX
+ return "barrierc";
+#endif
+}
+
+const char*
+ClientApp::daemonInfo() const
+{
+#if SYSAPI_WIN32
+ return "Allows another computer to share it's keyboard and mouse with this computer.";
+#elif SYSAPI_UNIX
+ return "";
+#endif
+}
+
+barrier::Screen*
+ClientApp::createScreen()
+{
+#if WINAPI_MSWINDOWS
+ return new barrier::Screen(new MSWindowsScreen(
+ false, args().m_noHooks, args().m_stopOnDeskSwitch, m_events), m_events);
+#elif WINAPI_XWINDOWS
+ return new barrier::Screen(new XWindowsScreen(
+ args().m_display, false, args().m_disableXInitThreads,
+ args().m_yscroll, m_events), m_events);
+#elif WINAPI_CARBON
+ return new barrier::Screen(new OSXScreen(m_events, false), m_events);
+#endif
+}
+
+void
+ClientApp::updateStatus()
+{
+ updateStatus("");
+}
+
+
+void
+ClientApp::updateStatus(const String& msg)
+{
+ if (m_taskBarReceiver)
+ {
+ m_taskBarReceiver->updateStatus(m_client, msg);
+ }
+}
+
+
+void
+ClientApp::resetRestartTimeout()
+{
+ // retry time can nolonger be changed
+ //s_retryTime = 0.0;
+}
+
+
+double
+ClientApp::nextRestartTimeout()
+{
+ // retry at a constant rate (Issue 52)
+ return RETRY_TIME;
+
+ /*
+ // choose next restart timeout. we start with rapid retries
+ // then slow down.
+ if (s_retryTime < 1.0) {
+ s_retryTime = 1.0;
+ }
+ else if (s_retryTime < 3.0) {
+ s_retryTime = 3.0;
+ }
+ else {
+ s_retryTime = 5.0;
+ }
+ return s_retryTime;
+ */
+}
+
+
+void
+ClientApp::handleScreenError(const Event&, void*)
+{
+ LOG((CLOG_CRIT "error on screen"));
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+
+barrier::Screen*
+ClientApp::openClientScreen()
+{
+ barrier::Screen* screen = createScreen();
+ screen->setEnableDragDrop(argsBase().m_enableDragDrop);
+ m_events->adoptHandler(m_events->forIScreen().error(),
+ screen->getEventTarget(),
+ new TMethodEventJob<ClientApp>(
+ this, &ClientApp::handleScreenError));
+ return screen;
+}
+
+
+void
+ClientApp::closeClientScreen(barrier::Screen* screen)
+{
+ if (screen != NULL) {
+ m_events->removeHandler(m_events->forIScreen().error(),
+ screen->getEventTarget());
+ delete screen;
+ }
+}
+
+
+void
+ClientApp::handleClientRestart(const Event&, void* vtimer)
+{
+ // discard old timer
+ EventQueueTimer* timer = static_cast<EventQueueTimer*>(vtimer);
+ m_events->deleteTimer(timer);
+ m_events->removeHandler(Event::kTimer, timer);
+
+ // reconnect
+ startClient();
+}
+
+
+void
+ClientApp::scheduleClientRestart(double retryTime)
+{
+ // install a timer and handler to retry later
+ LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
+ EventQueueTimer* timer = m_events->newOneShotTimer(retryTime, NULL);
+ m_events->adoptHandler(Event::kTimer, timer,
+ new TMethodEventJob<ClientApp>(this, &ClientApp::handleClientRestart, timer));
+}
+
+
+void
+ClientApp::handleClientConnected(const Event&, void*)
+{
+ LOG((CLOG_NOTE "connected to server"));
+ resetRestartTimeout();
+ updateStatus();
+}
+
+
+void
+ClientApp::handleClientFailed(const Event& e, void*)
+{
+ Client::FailInfo* info =
+ static_cast<Client::FailInfo*>(e.getData());
+
+ updateStatus(String("Failed to connect to server: ") + info->m_what);
+ if (!args().m_restartable || !info->m_retry) {
+ LOG((CLOG_ERR "failed to connect to server: %s", info->m_what.c_str()));
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ else {
+ LOG((CLOG_WARN "failed to connect to server: %s", info->m_what.c_str()));
+ if (!m_suspended) {
+ scheduleClientRestart(nextRestartTimeout());
+ }
+ }
+ delete info;
+}
+
+
+void
+ClientApp::handleClientDisconnected(const Event&, void*)
+{
+ LOG((CLOG_NOTE "disconnected from server"));
+ if (!args().m_restartable) {
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ else if (!m_suspended) {
+ scheduleClientRestart(nextRestartTimeout());
+ }
+ updateStatus();
+}
+
+Client*
+ClientApp::openClient(const String& name, const NetworkAddress& address,
+ barrier::Screen* screen)
+{
+ Client* client = new Client(
+ m_events,
+ name,
+ address,
+ new TCPSocketFactory(m_events, getSocketMultiplexer()),
+ screen,
+ args());
+
+ try {
+ m_events->adoptHandler(
+ m_events->forClient().connected(),
+ client->getEventTarget(),
+ new TMethodEventJob<ClientApp>(this, &ClientApp::handleClientConnected));
+
+ m_events->adoptHandler(
+ m_events->forClient().connectionFailed(),
+ client->getEventTarget(),
+ new TMethodEventJob<ClientApp>(this, &ClientApp::handleClientFailed));
+
+ m_events->adoptHandler(
+ m_events->forClient().disconnected(),
+ client->getEventTarget(),
+ new TMethodEventJob<ClientApp>(this, &ClientApp::handleClientDisconnected));
+
+ } catch (std::bad_alloc &ba) {
+ delete client;
+ throw ba;
+ }
+
+ return client;
+}
+
+
+void
+ClientApp::closeClient(Client* client)
+{
+ if (client == NULL) {
+ return;
+ }
+
+ m_events->removeHandler(m_events->forClient().connected(), client);
+ m_events->removeHandler(m_events->forClient().connectionFailed(), client);
+ m_events->removeHandler(m_events->forClient().disconnected(), client);
+ delete client;
+}
+
+int
+ClientApp::foregroundStartup(int argc, char** argv)
+{
+ initApp(argc, argv);
+
+ // never daemonize
+ return mainLoop();
+}
+
+bool
+ClientApp::startClient()
+{
+ double retryTime;
+ barrier::Screen* clientScreen = NULL;
+ try {
+ if (m_clientScreen == NULL) {
+ clientScreen = openClientScreen();
+ m_client = openClient(args().m_name,
+ *m_serverAddress, clientScreen);
+ m_clientScreen = clientScreen;
+ LOG((CLOG_NOTE "started client"));
+ }
+
+ m_client->connect();
+
+ updateStatus();
+ return true;
+ }
+ catch (XScreenUnavailable& e) {
+ LOG((CLOG_WARN "secondary screen unavailable: %s", e.what()));
+ closeClientScreen(clientScreen);
+ updateStatus(String("secondary screen unavailable: ") + e.what());
+ retryTime = e.getRetryTime();
+ }
+ catch (XScreenOpenFailure& e) {
+ LOG((CLOG_CRIT "failed to start client: %s", e.what()));
+ closeClientScreen(clientScreen);
+ return false;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "failed to start client: %s", e.what()));
+ closeClientScreen(clientScreen);
+ return false;
+ }
+
+ if (args().m_restartable) {
+ scheduleClientRestart(retryTime);
+ return true;
+ }
+ else {
+ // don't try again
+ return false;
+ }
+}
+
+
+void
+ClientApp::stopClient()
+{
+ closeClient(m_client);
+ closeClientScreen(m_clientScreen);
+ m_client = NULL;
+ m_clientScreen = NULL;
+}
+
+
+int
+ClientApp::mainLoop()
+{
+ // create socket multiplexer. this must happen after daemonization
+ // on unix because threads evaporate across a fork().
+ SocketMultiplexer multiplexer;
+ setSocketMultiplexer(&multiplexer);
+
+ // start client, etc
+ appUtil().startNode();
+
+ // init ipc client after node start, since create a new screen wipes out
+ // the event queue (the screen ctors call adoptBuffer).
+ if (argsBase().m_enableIpc) {
+ initIpcClient();
+ }
+
+ // run event loop. if startClient() failed we're supposed to retry
+ // later. the timer installed by startClient() will take care of
+ // that.
+ DAEMON_RUNNING(true);
+
+#if defined(MAC_OS_X_VERSION_10_7)
+
+ Thread thread(
+ new TMethodJob<ClientApp>(
+ this, &ClientApp::runEventsLoop,
+ NULL));
+
+ // wait until carbon loop is ready
+ OSXScreen* screen = dynamic_cast<OSXScreen*>(
+ m_clientScreen->getPlatformScreen());
+ screen->waitForCarbonLoop();
+
+ runCocoaApp();
+#else
+ m_events->loop();
+#endif
+
+ DAEMON_RUNNING(false);
+
+ // close down
+ LOG((CLOG_DEBUG1 "stopping client"));
+ stopClient();
+ updateStatus();
+ LOG((CLOG_NOTE "stopped client"));
+
+ if (argsBase().m_enableIpc) {
+ cleanupIpcClient();
+ }
+
+ return kExitSuccess;
+}
+
+static
+int
+daemonMainLoopStatic(int argc, const char** argv)
+{
+ return ClientApp::instance().daemonMainLoop(argc, argv);
+}
+
+int
+ClientApp::standardStartup(int argc, char** argv)
+{
+ initApp(argc, argv);
+
+ // daemonize if requested
+ if (args().m_daemon) {
+ return ARCH->daemonize(daemonName(), &daemonMainLoopStatic);
+ }
+ else {
+ return mainLoop();
+ }
+}
+
+int
+ClientApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
+{
+ // general initialization
+ m_serverAddress = new NetworkAddress;
+ args().m_pname = ARCH->getBasename(argv[0]);
+
+ // install caller's output filter
+ if (outputter != NULL) {
+ CLOG->insert(outputter);
+ }
+
+ int result;
+ try
+ {
+ // run
+ result = startup(argc, argv);
+ }
+ catch (...)
+ {
+ if (m_taskBarReceiver)
+ {
+ // done with task bar receiver
+ delete m_taskBarReceiver;
+ }
+
+ delete m_serverAddress;
+
+ throw;
+ }
+
+ return result;
+}
+
+void
+ClientApp::startNode()
+{
+ // start the client. if this return false then we've failed and
+ // we shouldn't retry.
+ LOG((CLOG_DEBUG1 "starting client"));
+ if (!startClient()) {
+ m_bye(kExitFailed);
+ }
+}
diff --git a/src/lib/barrier/ClientApp.h b/src/lib/barrier/ClientApp.h
new file mode 100644
index 0000000..777f3d3
--- /dev/null
+++ b/src/lib/barrier/ClientApp.h
@@ -0,0 +1,83 @@
+/*
+ * barrier -- 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 "barrier/App.h"
+
+namespace barrier { class Screen; }
+class Event;
+class Client;
+class NetworkAddress;
+class Thread;
+class ClientArgs;
+
+class ClientApp : public App {
+public:
+ ClientApp(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver);
+ virtual ~ClientApp();
+
+ // Parse client specific command line arguments.
+ void parseArgs(int argc, const char* const* argv);
+
+ // Prints help specific to client.
+ void help();
+
+ // Returns arguments that are common and for client.
+ ClientArgs& args() const { return (ClientArgs&)argsBase(); }
+
+ const char* daemonName() const;
+ const char* daemonInfo() const;
+
+ // TODO: move to server only (not supported on client)
+ void loadConfig() { }
+ bool loadConfig(const String& pathname) { return false; }
+
+ int foregroundStartup(int argc, char** argv);
+ int standardStartup(int argc, char** argv);
+ int runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup);
+ barrier::Screen* createScreen();
+ void updateStatus();
+ void updateStatus(const String& msg);
+ void resetRestartTimeout();
+ double nextRestartTimeout();
+ void handleScreenError(const Event&, void*);
+ barrier::Screen* openClientScreen();
+ void closeClientScreen(barrier::Screen* screen);
+ void handleClientRestart(const Event&, void* vtimer);
+ void scheduleClientRestart(double retryTime);
+ void handleClientConnected(const Event&, void*);
+ void handleClientFailed(const Event& e, void*);
+ void handleClientDisconnected(const Event&, void*);
+ Client* openClient(const String& name, const NetworkAddress& address,
+ barrier::Screen* screen);
+ void closeClient(Client* client);
+ bool startClient();
+ void stopClient();
+ int mainLoop();
+ void startNode();
+
+ static ClientApp& instance() { return (ClientApp&)App::instance(); }
+
+ Client* getClientPtr() { return m_client; }
+
+private:
+ Client* m_client;
+ barrier::Screen*m_clientScreen;
+ NetworkAddress* m_serverAddress;
+};
diff --git a/src/lib/barrier/ClientArgs.cpp b/src/lib/barrier/ClientArgs.cpp
new file mode 100644
index 0000000..5c9ed88
--- /dev/null
+++ b/src/lib/barrier/ClientArgs.cpp
@@ -0,0 +1,23 @@
+/*
+ * barrier -- 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 "barrier/ClientArgs.h"
+
+ClientArgs::ClientArgs() :
+ m_yscroll(0)
+{
+}
diff --git a/src/lib/barrier/ClientArgs.h b/src/lib/barrier/ClientArgs.h
new file mode 100644
index 0000000..70285fa
--- /dev/null
+++ b/src/lib/barrier/ClientArgs.h
@@ -0,0 +1,30 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include "barrier/ArgsBase.h"
+
+class NetworkAddress;
+
+class ClientArgs : public ArgsBase {
+public:
+ ClientArgs();
+
+public:
+ int m_yscroll;
+};
diff --git a/src/lib/barrier/ClientTaskBarReceiver.cpp b/src/lib/barrier/ClientTaskBarReceiver.cpp
new file mode 100644
index 0000000..2ea6566
--- /dev/null
+++ b/src/lib/barrier/ClientTaskBarReceiver.cpp
@@ -0,0 +1,141 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/ClientTaskBarReceiver.h"
+#include "client/Client.h"
+#include "mt/Lock.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "arch/Arch.h"
+#include "common/Version.h"
+
+//
+// ClientTaskBarReceiver
+//
+
+ClientTaskBarReceiver::ClientTaskBarReceiver(IEventQueue* events) :
+ m_state(kNotRunning),
+ m_events(events)
+{
+ // do nothing
+}
+
+ClientTaskBarReceiver::~ClientTaskBarReceiver()
+{
+ // do nothing
+}
+
+void
+ClientTaskBarReceiver::updateStatus(Client* client, const String& errorMsg)
+{
+ {
+ // update our status
+ m_errorMessage = errorMsg;
+ if (client == NULL) {
+ if (m_errorMessage.empty()) {
+ m_state = kNotRunning;
+ }
+ else {
+ m_state = kNotWorking;
+ }
+ }
+ else {
+ m_server = client->getServerAddress().getHostname();
+
+ if (client->isConnected()) {
+ m_state = kConnected;
+ }
+ else if (client->isConnecting()) {
+ m_state = kConnecting;
+ }
+ else {
+ m_state = kNotConnected;
+ }
+ }
+
+ // let subclasses have a go
+ onStatusChanged(client);
+ }
+
+ // tell task bar
+ ARCH->updateReceiver(this);
+}
+
+ClientTaskBarReceiver::EState
+ClientTaskBarReceiver::getStatus() const
+{
+ return m_state;
+}
+
+const String&
+ClientTaskBarReceiver::getErrorMessage() const
+{
+ return m_errorMessage;
+}
+
+void
+ClientTaskBarReceiver::quit()
+{
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+void
+ClientTaskBarReceiver::onStatusChanged(Client*)
+{
+ // do nothing
+}
+
+void
+ClientTaskBarReceiver::lock() const
+{
+ // do nothing
+}
+
+void
+ClientTaskBarReceiver::unlock() const
+{
+ // do nothing
+}
+
+std::string
+ClientTaskBarReceiver::getToolTip() const
+{
+ switch (m_state) {
+ case kNotRunning:
+ return barrier::string::sprintf("%s: Not running", kAppVersion);
+
+ case kNotWorking:
+ return barrier::string::sprintf("%s: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kNotConnected:
+ return barrier::string::sprintf("%s: Not connected: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kConnecting:
+ return barrier::string::sprintf("%s: Connecting to %s...",
+ kAppVersion, m_server.c_str());
+
+ case kConnected:
+ return barrier::string::sprintf("%s: Connected to %s",
+ kAppVersion, m_server.c_str());
+
+ default:
+ return "";
+ }
+}
diff --git a/src/lib/barrier/ClientTaskBarReceiver.h b/src/lib/barrier/ClientTaskBarReceiver.h
new file mode 100644
index 0000000..da15154
--- /dev/null
+++ b/src/lib/barrier/ClientTaskBarReceiver.h
@@ -0,0 +1,95 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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/>.
+ */
+
+#ifndef CCLIENTTASKBARRECEIVER_H
+#define CCLIENTTASKBARRECEIVER_H
+
+#include "base/String.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "base/log_outputters.h"
+#include "client/Client.h"
+
+class IEventQueue;
+
+//! Implementation of IArchTaskBarReceiver for the barrier server
+class ClientTaskBarReceiver : public IArchTaskBarReceiver {
+public:
+ ClientTaskBarReceiver(IEventQueue* events);
+ virtual ~ClientTaskBarReceiver();
+
+ //! @name manipulators
+ //@{
+
+ //! Update status
+ /*!
+ Determine the status and query required information from the client.
+ */
+ void updateStatus(Client*, const String& errorMsg);
+
+ void updateStatus(INode* n, const String& errorMsg) { updateStatus((Client*)n, errorMsg); }
+
+ //@}
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus() = 0;
+ virtual void runMenu(int x, int y) = 0;
+ virtual void primaryAction() = 0;
+ virtual void lock() const;
+ virtual void unlock() const;
+ virtual const Icon getIcon() const = 0;
+ virtual std::string getToolTip() const;
+ virtual void cleanup() {}
+
+protected:
+ enum EState {
+ kNotRunning,
+ kNotWorking,
+ kNotConnected,
+ kConnecting,
+ kConnected,
+ kMaxState
+ };
+
+ //! Get status
+ EState getStatus() const;
+
+ //! Get error message
+ const String& getErrorMessage() const;
+
+ //! Quit app
+ /*!
+ Causes the application to quit gracefully
+ */
+ void quit();
+
+ //! Status change notification
+ /*!
+ Called when status changes. The default implementation does nothing.
+ */
+ virtual void onStatusChanged(Client* client);
+
+private:
+ EState m_state;
+ String m_errorMessage;
+ String m_server;
+ IEventQueue* m_events;
+};
+
+IArchTaskBarReceiver* createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events);
+
+#endif
diff --git a/src/lib/barrier/Clipboard.cpp b/src/lib/barrier/Clipboard.cpp
new file mode 100644
index 0000000..a6a166d
--- /dev/null
+++ b/src/lib/barrier/Clipboard.cpp
@@ -0,0 +1,118 @@
+/*
+ * barrier -- 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 "barrier/Clipboard.h"
+
+//
+// Clipboard
+//
+
+Clipboard::Clipboard() :
+ m_open(false),
+ m_owner(false)
+{
+ open(0);
+ empty();
+ close();
+}
+
+Clipboard::~Clipboard()
+{
+ // do nothing
+}
+
+bool
+Clipboard::empty()
+{
+ assert(m_open);
+
+ // clear all data
+ for (SInt32 index = 0; index < kNumFormats; ++index) {
+ m_data[index] = "";
+ m_added[index] = false;
+ }
+
+ // save time
+ m_timeOwned = m_time;
+
+ // we're the owner now
+ m_owner = true;
+
+ return true;
+}
+
+void
+Clipboard::add(EFormat format, const String& data)
+{
+ assert(m_open);
+ assert(m_owner);
+
+ m_data[format] = data;
+ m_added[format] = true;
+}
+
+bool
+Clipboard::open(Time time) const
+{
+ assert(!m_open);
+
+ m_open = true;
+ m_time = time;
+
+ return true;
+}
+
+void
+Clipboard::close() const
+{
+ assert(m_open);
+
+ m_open = false;
+}
+
+Clipboard::Time
+Clipboard::getTime() const
+{
+ return m_timeOwned;
+}
+
+bool
+Clipboard::has(EFormat format) const
+{
+ assert(m_open);
+ return m_added[format];
+}
+
+String
+Clipboard::get(EFormat format) const
+{
+ assert(m_open);
+ return m_data[format];
+}
+
+void
+Clipboard::unmarshall(const String& data, Time time)
+{
+ IClipboard::unmarshall(this, data, time);
+}
+
+String
+Clipboard::marshall() const
+{
+ return IClipboard::marshall(this);
+}
diff --git a/src/lib/barrier/Clipboard.h b/src/lib/barrier/Clipboard.h
new file mode 100644
index 0000000..23bea75
--- /dev/null
+++ b/src/lib/barrier/Clipboard.h
@@ -0,0 +1,71 @@
+/*
+ * barrier -- 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 "barrier/IClipboard.h"
+
+//! Memory buffer clipboard
+/*!
+This class implements a clipboard that stores data in memory.
+*/
+class Clipboard : public IClipboard {
+public:
+ Clipboard();
+ virtual ~Clipboard();
+
+ //! @name manipulators
+ //@{
+
+ //! Unmarshall clipboard data
+ /*!
+ Extract marshalled clipboard data and store it in this clipboard.
+ Sets the clipboard time to \c time.
+ */
+ void unmarshall(const String& data, Time time);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Marshall clipboard data
+ /*!
+ Merge this clipboard's data into a single buffer that can be later
+ unmarshalled to restore the clipboard and return the buffer.
+ */
+ String marshall() const;
+
+ //@}
+
+ // IClipboard overrides
+ virtual bool empty();
+ virtual void add(EFormat, const String& data);
+ virtual bool open(Time) const;
+ virtual void close() const;
+ virtual Time getTime() const;
+ virtual bool has(EFormat) const;
+ virtual String get(EFormat) const;
+
+private:
+ mutable bool m_open;
+ mutable Time m_time;
+ bool m_owner;
+ Time m_timeOwned;
+ bool m_added[kNumFormats];
+ String m_data[kNumFormats];
+};
diff --git a/src/lib/barrier/ClipboardChunk.cpp b/src/lib/barrier/ClipboardChunk.cpp
new file mode 100644
index 0000000..bc71471
--- /dev/null
+++ b/src/lib/barrier/ClipboardChunk.cpp
@@ -0,0 +1,154 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "barrier/ClipboardChunk.h"
+
+#include "barrier/ProtocolUtil.h"
+#include "barrier/protocol_types.h"
+#include "io/IStream.h"
+#include "base/Log.h"
+#include <cstring>
+
+size_t ClipboardChunk::s_expectedSize = 0;
+
+ClipboardChunk::ClipboardChunk(size_t size) :
+ Chunk(size)
+{
+ m_dataSize = size - CLIPBOARD_CHUNK_META_SIZE;
+}
+
+ClipboardChunk*
+ClipboardChunk::start(
+ ClipboardID id,
+ UInt32 sequence,
+ const String& size)
+{
+ size_t sizeLength = size.size();
+ ClipboardChunk* start = new ClipboardChunk(sizeLength + CLIPBOARD_CHUNK_META_SIZE);
+ char* chunk = start->m_chunk;
+
+ chunk[0] = id;
+ std::memcpy (&chunk[1], &sequence, 4);
+ chunk[5] = kDataStart;
+ memcpy(&chunk[6], size.c_str(), sizeLength);
+ chunk[sizeLength + CLIPBOARD_CHUNK_META_SIZE - 1] = '\0';
+
+ return start;
+}
+
+ClipboardChunk*
+ClipboardChunk::data(
+ ClipboardID id,
+ UInt32 sequence,
+ const String& data)
+{
+ size_t dataSize = data.size();
+ ClipboardChunk* chunk = new ClipboardChunk(dataSize + CLIPBOARD_CHUNK_META_SIZE);
+ char* chunkData = chunk->m_chunk;
+
+ chunkData[0] = id;
+ std::memcpy (&chunkData[1], &sequence, 4);
+ chunkData[5] = kDataChunk;
+ memcpy(&chunkData[6], data.c_str(), dataSize);
+ chunkData[dataSize + CLIPBOARD_CHUNK_META_SIZE - 1] = '\0';
+
+ return chunk;
+}
+
+ClipboardChunk*
+ClipboardChunk::end(ClipboardID id, UInt32 sequence)
+{
+ ClipboardChunk* end = new ClipboardChunk(CLIPBOARD_CHUNK_META_SIZE);
+ char* chunk = end->m_chunk;
+
+ chunk[0] = id;
+ std::memcpy (&chunk[1], &sequence, 4);
+ chunk[5] = kDataEnd;
+ chunk[CLIPBOARD_CHUNK_META_SIZE - 1] = '\0';
+
+ return end;
+}
+
+int
+ClipboardChunk::assemble(barrier::IStream* stream,
+ String& dataCached,
+ ClipboardID& id,
+ UInt32& sequence)
+{
+ UInt8 mark;
+ String data;
+
+ if (!ProtocolUtil::readf(stream, kMsgDClipboard + 4, &id, &sequence, &mark, &data)) {
+ return kError;
+ }
+
+ if (mark == kDataStart) {
+ s_expectedSize = barrier::string::stringToSizeType(data);
+ LOG((CLOG_DEBUG "start receiving clipboard data"));
+ dataCached.clear();
+ return kStart;
+ }
+ else if (mark == kDataChunk) {
+ dataCached.append(data);
+ return kNotFinish;
+ }
+ else if (mark == kDataEnd) {
+ // validate
+ if (id >= kClipboardEnd) {
+ return kError;
+ }
+ else if (s_expectedSize != dataCached.size()) {
+ LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", s_expectedSize, dataCached.size()));
+ return kError;
+ }
+ return kFinish;
+ }
+
+ LOG((CLOG_ERR "clipboard transmission failed: unknown error"));
+ return kError;
+}
+
+void
+ClipboardChunk::send(barrier::IStream* stream, void* data)
+{
+ ClipboardChunk* clipboardData = static_cast<ClipboardChunk*>(data);
+
+ LOG((CLOG_DEBUG1 "sending clipboard chunk"));
+
+ char* chunk = clipboardData->m_chunk;
+ ClipboardID id = chunk[0];
+ UInt32 sequence;
+ std::memcpy (&sequence, &chunk[1], 4);
+ UInt8 mark = chunk[5];
+ String dataChunk(&chunk[6], clipboardData->m_dataSize);
+
+ switch (mark) {
+ case kDataStart:
+ LOG((CLOG_DEBUG2 "sending clipboard chunk start: size=%s", dataChunk.c_str()));
+ break;
+
+ case kDataChunk:
+ LOG((CLOG_DEBUG2 "sending clipboard chunk data: size=%i", dataChunk.size()));
+ break;
+
+ case kDataEnd:
+ LOG((CLOG_DEBUG2 "sending clipboard finished"));
+ break;
+ }
+
+ ProtocolUtil::writef(stream, kMsgDClipboard, id, sequence, mark, &dataChunk);
+}
diff --git a/src/lib/barrier/ClipboardChunk.h b/src/lib/barrier/ClipboardChunk.h
new file mode 100644
index 0000000..6402aca
--- /dev/null
+++ b/src/lib/barrier/ClipboardChunk.h
@@ -0,0 +1,60 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+#include "barrier/Chunk.h"
+#include "barrier/clipboard_types.h"
+#include "base/String.h"
+#include "common/basic_types.h"
+
+#define CLIPBOARD_CHUNK_META_SIZE 7
+
+namespace barrier {
+class IStream;
+};
+
+class ClipboardChunk : public Chunk {
+public:
+ ClipboardChunk(size_t size);
+
+ static ClipboardChunk*
+ start(
+ ClipboardID id,
+ UInt32 sequence,
+ const String& size);
+ static ClipboardChunk*
+ data(
+ ClipboardID id,
+ UInt32 sequence,
+ const String& data);
+ static ClipboardChunk*
+ end(ClipboardID id, UInt32 sequence);
+
+ static int assemble(
+ barrier::IStream* stream,
+ String& dataCached,
+ ClipboardID& id,
+ UInt32& sequence);
+
+ static void send(barrier::IStream* stream, void* data);
+
+ static size_t getExpectedSize() { return s_expectedSize; }
+
+private:
+ static size_t s_expectedSize;
+};
diff --git a/src/lib/barrier/DragInformation.cpp b/src/lib/barrier/DragInformation.cpp
new file mode 100644
index 0000000..db28f3d
--- /dev/null
+++ b/src/lib/barrier/DragInformation.cpp
@@ -0,0 +1,158 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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 "barrier/DragInformation.h"
+#include "base/Log.h"
+
+#include <fstream>
+#include <sstream>
+#include <stdexcept>
+
+using namespace std;
+
+DragInformation::DragInformation() :
+ m_filename(),
+ m_filesize(0)
+{
+}
+
+void
+DragInformation::parseDragInfo(DragFileList& dragFileList, UInt32 fileNum, String data)
+{
+ size_t startPos = 0;
+ size_t findResult1 = 0;
+ size_t findResult2 = 0;
+ dragFileList.clear();
+ String slash("\\");
+ if (data.find("/", startPos) != string::npos) {
+ slash = "/";
+ }
+
+ UInt32 index = 0;
+ while (index < fileNum) {
+ findResult1 = data.find(',', startPos);
+ findResult2 = data.find_last_of(slash, findResult1);
+
+ if (findResult1 == startPos) {
+ //TODO: file number does not match, something goes wrong
+ break;
+ }
+
+ // set filename
+ if (findResult1 - findResult2 > 1) {
+ String filename = data.substr(findResult2 + 1,
+ findResult1 - findResult2 - 1);
+ DragInformation di;
+ di.setFilename(filename);
+ dragFileList.push_back(di);
+ }
+ startPos = findResult1 + 1;
+
+ //set filesize
+ findResult2 = data.find(',', startPos);
+ if (findResult2 - findResult1 > 1) {
+ String filesize = data.substr(findResult1 + 1,
+ findResult2 - findResult1 - 1);
+ size_t size = stringToNum(filesize);
+ dragFileList.at(index).setFilesize(size);
+ }
+ startPos = findResult1 + 1;
+
+ ++index;
+ }
+
+ LOG((CLOG_DEBUG "drag info received, total drag file number: %i",
+ dragFileList.size()));
+
+ for (size_t i = 0; i < dragFileList.size(); ++i) {
+ LOG((CLOG_DEBUG "dragging file %i name: %s",
+ i + 1,
+ dragFileList.at(i).getFilename().c_str()));
+ }
+}
+
+String
+DragInformation::getDragFileExtension(String filename)
+{
+ size_t findResult = string::npos;
+ findResult = filename.find_last_of(".", filename.size());
+ if (findResult != string::npos) {
+ return filename.substr(findResult + 1, filename.size() - findResult - 1);
+ }
+ else {
+ return "";
+ }
+}
+
+int
+DragInformation::setupDragInfo(DragFileList& fileList, String& output)
+{
+ int size = static_cast<int>(fileList.size());
+ for (int i = 0; i < size; ++i) {
+ output.append(fileList.at(i).getFilename());
+ output.append(",");
+ String filesize = getFileSize(fileList.at(i).getFilename());
+ output.append(filesize);
+ output.append(",");
+ }
+ return size;
+}
+
+bool
+DragInformation::isFileValid(String filename)
+{
+ bool result = false;
+ std::fstream file(filename.c_str(), ios::in|ios::binary);
+
+ if (file.is_open()) {
+ result = true;
+ }
+
+ file. close();
+
+ return result;
+}
+
+size_t
+DragInformation::stringToNum(String& str)
+{
+ istringstream iss(str.c_str());
+ size_t size;
+ iss >> size;
+ return size;
+}
+
+String
+DragInformation::getFileSize(String& filename)
+{
+ std::fstream file(filename.c_str(), ios::in|ios::binary);
+
+ if (!file.is_open()) {
+ throw std::runtime_error("failed to get file size");
+ }
+
+ // check file size
+ file.seekg (0, std::ios::end);
+ size_t size = (size_t)file.tellg();
+
+ stringstream ss;
+ ss << size;
+
+ file. close();
+
+ return ss.str();
+}
diff --git a/src/lib/barrier/DragInformation.h b/src/lib/barrier/DragInformation.h
new file mode 100644
index 0000000..b985bd1
--- /dev/null
+++ b/src/lib/barrier/DragInformation.h
@@ -0,0 +1,53 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#include "common/stdvector.h"
+#include "base/String.h"
+#include "base/EventTypes.h"
+
+class DragInformation;
+typedef std::vector<DragInformation> DragFileList;
+
+class DragInformation {
+public:
+ DragInformation();
+ ~DragInformation() { }
+
+ String& getFilename() { return m_filename; }
+ void setFilename(String& name) { m_filename = name; }
+ size_t getFilesize() { return m_filesize; }
+ void setFilesize(size_t size) { m_filesize = size; }
+
+ static void parseDragInfo(DragFileList& dragFileList, UInt32 fileNum, String data);
+ static String getDragFileExtension(String filename);
+ // helper function to setup drag info
+ // example: filename1,filesize1,filename2,filesize2,
+ // return file count
+ static int setupDragInfo(DragFileList& fileList, String& output);
+
+ static bool isFileValid(String filename);
+
+private:
+ static size_t stringToNum(String& str);
+ static String getFileSize(String& filename);
+
+private:
+ String m_filename;
+ size_t m_filesize;
+};
diff --git a/src/lib/barrier/DropHelper.cpp b/src/lib/barrier/DropHelper.cpp
new file mode 100644
index 0000000..ee5e5ee
--- /dev/null
+++ b/src/lib/barrier/DropHelper.cpp
@@ -0,0 +1,53 @@
+/*
+ * barrier -- 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 "barrier/DropHelper.h"
+
+#include "base/Log.h"
+
+#include <fstream>
+
+void
+DropHelper::writeToDir(const String& destination, DragFileList& fileList, String& data)
+{
+ LOG((CLOG_DEBUG "dropping file, files=%i target=%s", fileList.size(), destination.c_str()));
+
+ if (!destination.empty() && fileList.size() > 0) {
+ std::fstream file;
+ String dropTarget = destination;
+#ifdef SYSAPI_WIN32
+ dropTarget.append("\\");
+#else
+ dropTarget.append("/");
+#endif
+ dropTarget.append(fileList.at(0).getFilename());
+ file.open(dropTarget.c_str(), std::ios::out | std::ios::binary);
+ if (!file.is_open()) {
+ LOG((CLOG_ERR "drop file failed: can not open %s", dropTarget.c_str()));
+ }
+
+ file.write(data.c_str(), data.size());
+ file.close();
+
+ LOG((CLOG_DEBUG "%s is saved to %s", fileList.at(0).getFilename().c_str(), destination.c_str()));
+
+ fileList.clear();
+ }
+ else {
+ LOG((CLOG_ERR "drop file failed: drop target is empty"));
+ }
+}
diff --git a/src/lib/barrier/DropHelper.h b/src/lib/barrier/DropHelper.h
new file mode 100644
index 0000000..67facbb
--- /dev/null
+++ b/src/lib/barrier/DropHelper.h
@@ -0,0 +1,27 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include "barrier/DragInformation.h"
+#include "base/String.h"
+
+class DropHelper {
+public:
+ static void writeToDir(const String& destination,
+ DragFileList& fileList, String& data);
+};
diff --git a/src/lib/barrier/FileChunk.cpp b/src/lib/barrier/FileChunk.cpp
new file mode 100644
index 0000000..3a98568
--- /dev/null
+++ b/src/lib/barrier/FileChunk.cpp
@@ -0,0 +1,156 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "barrier/FileChunk.h"
+
+#include "barrier/ProtocolUtil.h"
+#include "barrier/protocol_types.h"
+#include "io/IStream.h"
+#include "base/Stopwatch.h"
+#include "base/Log.h"
+
+static const UInt16 kIntervalThreshold = 1;
+
+FileChunk::FileChunk(size_t size) :
+ Chunk(size)
+{
+ m_dataSize = size - FILE_CHUNK_META_SIZE;
+}
+
+FileChunk*
+FileChunk::start(const String& size)
+{
+ size_t sizeLength = size.size();
+ FileChunk* start = new FileChunk(sizeLength + FILE_CHUNK_META_SIZE);
+ char* chunk = start->m_chunk;
+ chunk[0] = kDataStart;
+ memcpy(&chunk[1], size.c_str(), sizeLength);
+ chunk[sizeLength + 1] = '\0';
+
+ return start;
+}
+
+FileChunk*
+FileChunk::data(UInt8* data, size_t dataSize)
+{
+ FileChunk* chunk = new FileChunk(dataSize + FILE_CHUNK_META_SIZE);
+ char* chunkData = chunk->m_chunk;
+ chunkData[0] = kDataChunk;
+ memcpy(&chunkData[1], data, dataSize);
+ chunkData[dataSize + 1] = '\0';
+
+ return chunk;
+}
+
+FileChunk*
+FileChunk::end()
+{
+ FileChunk* end = new FileChunk(FILE_CHUNK_META_SIZE);
+ char* chunk = end->m_chunk;
+ chunk[0] = kDataEnd;
+ chunk[1] = '\0';
+
+ return end;
+}
+
+int
+FileChunk::assemble(barrier::IStream* stream, String& dataReceived, size_t& expectedSize)
+{
+ // parse
+ UInt8 mark = 0;
+ String content;
+ static size_t receivedDataSize;
+ static double elapsedTime;
+ static Stopwatch stopwatch;
+
+ if (!ProtocolUtil::readf(stream, kMsgDFileTransfer + 4, &mark, &content)) {
+ return kError;
+ }
+
+ switch (mark) {
+ case kDataStart:
+ dataReceived.clear();
+ expectedSize = barrier::string::stringToSizeType(content);
+ receivedDataSize = 0;
+ elapsedTime = 0;
+ stopwatch.reset();
+
+ if (CLOG->getFilter() >= kDEBUG2) {
+ LOG((CLOG_DEBUG2 "recv file size=%s", content.c_str()));
+ stopwatch.start();
+ }
+ return kStart;
+
+ case kDataChunk:
+ dataReceived.append(content);
+ if (CLOG->getFilter() >= kDEBUG2) {
+ LOG((CLOG_DEBUG2 "recv file chunck size=%i", content.size()));
+ double interval = stopwatch.getTime();
+ receivedDataSize += content.size();
+ LOG((CLOG_DEBUG2 "recv file interval=%f s", interval));
+ if (interval >= kIntervalThreshold) {
+ double averageSpeed = receivedDataSize / interval / 1000;
+ LOG((CLOG_DEBUG2 "recv file average speed=%f kb/s", averageSpeed));
+
+ receivedDataSize = 0;
+ elapsedTime += interval;
+ stopwatch.reset();
+ }
+ }
+ return kNotFinish;
+
+ case kDataEnd:
+ if (expectedSize != dataReceived.size()) {
+ LOG((CLOG_ERR "corrupted clipboard data, expected size=%d actual size=%d", expectedSize, dataReceived.size()));
+ return kError;
+ }
+
+ if (CLOG->getFilter() >= kDEBUG2) {
+ LOG((CLOG_DEBUG2 "file transfer finished"));
+ elapsedTime += stopwatch.getTime();
+ double averageSpeed = expectedSize / elapsedTime / 1000;
+ LOG((CLOG_DEBUG2 "file transfer finished: total time consumed=%f s", elapsedTime));
+ LOG((CLOG_DEBUG2 "file transfer finished: total data received=%i kb", expectedSize / 1000));
+ LOG((CLOG_DEBUG2 "file transfer finished: total average speed=%f kb/s", averageSpeed));
+ }
+ return kFinish;
+ }
+
+ return kError;
+}
+
+void
+FileChunk::send(barrier::IStream* stream, UInt8 mark, char* data, size_t dataSize)
+{
+ String chunk(data, dataSize);
+
+ switch (mark) {
+ case kDataStart:
+ LOG((CLOG_DEBUG2 "sending file chunk start: size=%s", data));
+ break;
+
+ case kDataChunk:
+ LOG((CLOG_DEBUG2 "sending file chunk: size=%i", chunk.size()));
+ break;
+
+ case kDataEnd:
+ LOG((CLOG_DEBUG2 "sending file finished"));
+ break;
+ }
+
+ ProtocolUtil::writef(stream, kMsgDFileTransfer, mark, &chunk);
+}
diff --git a/src/lib/barrier/FileChunk.h b/src/lib/barrier/FileChunk.h
new file mode 100644
index 0000000..bdc2f64
--- /dev/null
+++ b/src/lib/barrier/FileChunk.h
@@ -0,0 +1,46 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+#include "barrier/Chunk.h"
+#include "base/String.h"
+#include "common/basic_types.h"
+
+#define FILE_CHUNK_META_SIZE 2
+
+namespace barrier {
+class IStream;
+};
+
+class FileChunk : public Chunk {
+public:
+ FileChunk(size_t size);
+
+ static FileChunk* start(const String& size);
+ static FileChunk* data(UInt8* data, size_t dataSize);
+ static FileChunk* end();
+ static int assemble(
+ barrier::IStream* stream,
+ String& dataCached,
+ size_t& expectedSize);
+ static void send(
+ barrier::IStream* stream,
+ UInt8 mark,
+ char* data,
+ size_t dataSize);
+};
diff --git a/src/lib/barrier/IApp.h b/src/lib/barrier/IApp.h
new file mode 100644
index 0000000..3a8cd56
--- /dev/null
+++ b/src/lib/barrier/IApp.h
@@ -0,0 +1,47 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "common/IInterface.h"
+
+typedef int (*StartupFunc)(int, char**);
+
+class ILogOutputter;
+class ArgsBase;
+class IArchTaskBarReceiver;
+namespace barrier { class Screen; }
+class IEventQueue;
+
+class IApp : public IInterface
+{
+public:
+ virtual void setByeFunc(void(*bye)(int)) = 0;
+ virtual ArgsBase& argsBase() const = 0;
+ virtual int standardStartup(int argc, char** argv) = 0;
+ virtual int runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup) = 0;
+ virtual void startNode() = 0;
+ virtual IArchTaskBarReceiver* taskBarReceiver() const = 0;
+ virtual void bye(int error) = 0;
+ virtual int mainLoop() = 0;
+ virtual void initApp(int argc, const char** argv) = 0;
+ virtual const char* daemonName() const = 0;
+ virtual int foregroundStartup(int argc, char** argv) = 0;
+ virtual barrier::Screen* createScreen() = 0;
+ virtual IEventQueue* getEvents() const = 0;
+};
diff --git a/src/lib/barrier/IAppUtil.h b/src/lib/barrier/IAppUtil.h
new file mode 100644
index 0000000..39df65d
--- /dev/null
+++ b/src/lib/barrier/IAppUtil.h
@@ -0,0 +1,31 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+#include "barrier/IApp.h"
+
+class IAppUtil : public IInterface {
+public:
+ virtual void adoptApp(IApp* app) = 0;
+ virtual IApp& app() const = 0;
+ virtual int run(int argc, char** argv) = 0;
+ virtual void beforeAppExit() = 0;
+ virtual void startNode() = 0;
+};
diff --git a/src/lib/barrier/IClient.h b/src/lib/barrier/IClient.h
new file mode 100644
index 0000000..d9b2194
--- /dev/null
+++ b/src/lib/barrier/IClient.h
@@ -0,0 +1,176 @@
+/*
+ * barrier -- 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 "barrier/clipboard_types.h"
+#include "barrier/IScreen.h"
+#include "barrier/key_types.h"
+#include "barrier/mouse_types.h"
+#include "barrier/option_types.h"
+#include "base/String.h"
+
+//! Client interface
+/*!
+This interface defines the methods necessary for the server to
+communicate with a client.
+*/
+class IClient : public IScreen {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Enter screen
+ /*!
+ Enter the screen. The cursor should be warped to \p xAbs,yAbs.
+ \p mask is the expected toggle button state and the client should
+ update its state to match. \p forScreensaver is true iff the
+ screen is being entered because the screen saver is starting.
+ Subsequent clipboard events should report \p seqNum.
+ */
+ virtual void enter(SInt32 xAbs, SInt32 yAbs,
+ UInt32 seqNum, KeyModifierMask mask,
+ bool forScreensaver) = 0;
+
+ //! Leave screen
+ /*!
+ Leave the screen. Return false iff the user may not leave the
+ client's screen (because, for example, a button is down).
+ */
+ virtual bool leave() = 0;
+
+ //! Set clipboard
+ /*!
+ Update the client's clipboard. This implies that the client's
+ clipboard is now up to date. If the client's clipboard was
+ already known to be up to date then this may do nothing. \c data
+ has marshalled clipboard data.
+ */
+ virtual void setClipboard(ClipboardID, const IClipboard*) = 0;
+
+ //! Grab clipboard
+ /*!
+ Grab (i.e. take ownership of) the client's clipboard. Since this
+ is called when another client takes ownership of the clipboard it
+ implies that the client's clipboard is out of date.
+ */
+ virtual void grabClipboard(ClipboardID) = 0;
+
+ //! Mark clipboard dirty
+ /*!
+ Mark the client's clipboard as dirty (out of date) or clean (up to
+ date).
+ */
+ virtual void setClipboardDirty(ClipboardID, bool dirty) = 0;
+
+ //! Notify of key press
+ /*!
+ Synthesize key events to generate a press of key \c id. If possible
+ match the given modifier mask. The KeyButton identifies the physical
+ key on the server that generated this key down. The client must
+ ensure that a key up or key repeat that uses the same KeyButton will
+ synthesize an up or repeat for the same client key synthesized by
+ keyDown().
+ */
+ virtual void keyDown(KeyID id, KeyModifierMask, KeyButton) = 0;
+
+ //! Notify of key repeat
+ /*!
+ Synthesize key events to generate a press and release of key \c id
+ \c count times. If possible match the given modifier mask.
+ */
+ virtual void keyRepeat(KeyID id, KeyModifierMask,
+ SInt32 count, KeyButton) = 0;
+
+ //! Notify of key release
+ /*!
+ Synthesize key events to generate a release of key \c id. If possible
+ match the given modifier mask.
+ */
+ virtual void keyUp(KeyID id, KeyModifierMask, KeyButton) = 0;
+
+ //! Notify of mouse press
+ /*!
+ Synthesize mouse events to generate a press of mouse button \c id.
+ */
+ virtual void mouseDown(ButtonID id) = 0;
+
+ //! Notify of mouse release
+ /*!
+ Synthesize mouse events to generate a release of mouse button \c id.
+ */
+ virtual void mouseUp(ButtonID id) = 0;
+
+ //! Notify of mouse motion
+ /*!
+ Synthesize mouse events to generate mouse motion to the absolute
+ screen position \c xAbs,yAbs.
+ */
+ virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0;
+
+ //! Notify of mouse motion
+ /*!
+ Synthesize mouse events to generate mouse motion by the relative
+ amount \c xRel,yRel.
+ */
+ virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0;
+
+ //! Notify of mouse wheel motion
+ /*!
+ Synthesize mouse events to generate mouse wheel motion of \c xDelta
+ and \c yDelta. Deltas are positive for motion away from the user or
+ to the right and negative for motion towards the user or to the left.
+ Each wheel click should generate a delta of +/-120.
+ */
+ virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta) = 0;
+
+ //! Notify of screen saver change
+ virtual void screensaver(bool activate) = 0;
+
+ //! Notify of options changes
+ /*!
+ Reset all options to their default values.
+ */
+ virtual void resetOptions() = 0;
+
+ //! Notify of options changes
+ /*!
+ Set options to given values. Ignore unknown options and don't
+ modify our options that aren't given in \c options.
+ */
+ virtual void setOptions(const OptionsList& options) = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get client name
+ /*!
+ Return the client's name.
+ */
+ virtual String getName() const = 0;
+
+ //@}
+
+ // IScreen overrides
+ virtual void* getEventTarget() const = 0;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const = 0;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
+};
diff --git a/src/lib/barrier/IClipboard.cpp b/src/lib/barrier/IClipboard.cpp
new file mode 100644
index 0000000..19b4b56
--- /dev/null
+++ b/src/lib/barrier/IClipboard.cpp
@@ -0,0 +1,168 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/IClipboard.h"
+#include "common/stdvector.h"
+
+//
+// IClipboard
+//
+
+void
+IClipboard::unmarshall(IClipboard* clipboard, const String& data, Time time)
+{
+ assert(clipboard != NULL);
+
+ const char* index = data.data();
+
+ if (clipboard->open(time)) {
+ // clear existing data
+ clipboard->empty();
+
+ // read the number of formats
+ const UInt32 numFormats = readUInt32(index);
+ index += 4;
+
+ // read each format
+ for (UInt32 i = 0; i < numFormats; ++i) {
+ // get the format id
+ IClipboard::EFormat format =
+ static_cast<IClipboard::EFormat>(readUInt32(index));
+ index += 4;
+
+ // get the size of the format data
+ UInt32 size = readUInt32(index);
+ index += 4;
+
+ // save the data if it's a known format. if either the client
+ // or server supports more clipboard formats than the other
+ // then one of them will get a format >= kNumFormats here.
+ if (format <IClipboard::kNumFormats) {
+ clipboard->add(format, String(index, size));
+ }
+ index += size;
+ }
+
+ // done
+ clipboard->close();
+ }
+}
+
+String
+IClipboard::marshall(const IClipboard* clipboard)
+{
+ // return data format:
+ // 4 bytes => number of formats included
+ // 4 bytes => format enum
+ // 4 bytes => clipboard data size n
+ // n bytes => clipboard data
+ // back to the second 4 bytes if there is another format
+
+ assert(clipboard != NULL);
+
+ String data;
+
+ std::vector<String> formatData;
+ formatData.resize(IClipboard::kNumFormats);
+ // FIXME -- use current time
+ if (clipboard->open(0)) {
+
+ // compute size of marshalled data
+ UInt32 size = 4;
+ UInt32 numFormats = 0;
+ for (UInt32 format = 0; format != IClipboard::kNumFormats; ++format) {
+ if (clipboard->has(static_cast<IClipboard::EFormat>(format))) {
+ ++numFormats;
+ formatData[format] =
+ clipboard->get(static_cast<IClipboard::EFormat>(format));
+ size += 4 + 4 + (UInt32)formatData[format].size();
+ }
+ }
+
+ // allocate space
+ data.reserve(size);
+
+ // marshall the data
+ writeUInt32(&data, numFormats);
+ for (UInt32 format = 0; format != IClipboard::kNumFormats; ++format) {
+ if (clipboard->has(static_cast<IClipboard::EFormat>(format))) {
+ writeUInt32(&data, format);
+ writeUInt32(&data, (UInt32)formatData[format].size());
+ data += formatData[format];
+ }
+ }
+ clipboard->close();
+ }
+
+ return data;
+}
+
+bool
+IClipboard::copy(IClipboard* dst, const IClipboard* src)
+{
+ assert(dst != NULL);
+ assert(src != NULL);
+
+ return copy(dst, src, src->getTime());
+}
+
+bool
+IClipboard::copy(IClipboard* dst, const IClipboard* src, Time time)
+{
+ assert(dst != NULL);
+ assert(src != NULL);
+
+ bool success = false;
+ if (src->open(time)) {
+ if (dst->open(time)) {
+ if (dst->empty()) {
+ for (SInt32 format = 0;
+ format != IClipboard::kNumFormats; ++format) {
+ IClipboard::EFormat eFormat = (IClipboard::EFormat)format;
+ if (src->has(eFormat)) {
+ dst->add(eFormat, src->get(eFormat));
+ }
+ }
+ success = true;
+ }
+ dst->close();
+ }
+ src->close();
+ }
+
+ return success;
+}
+
+UInt32
+IClipboard::readUInt32(const char* buf)
+{
+ const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
+ return (static_cast<UInt32>(ubuf[0]) << 24) |
+ (static_cast<UInt32>(ubuf[1]) << 16) |
+ (static_cast<UInt32>(ubuf[2]) << 8) |
+ static_cast<UInt32>(ubuf[3]);
+}
+
+void
+IClipboard::writeUInt32(String* buf, UInt32 v)
+{
+ *buf += static_cast<UInt8>((v >> 24) & 0xff);
+ *buf += static_cast<UInt8>((v >> 16) & 0xff);
+ *buf += static_cast<UInt8>((v >> 8) & 0xff);
+ *buf += static_cast<UInt8>( v & 0xff);
+}
diff --git a/src/lib/barrier/IClipboard.h b/src/lib/barrier/IClipboard.h
new file mode 100644
index 0000000..e11b264
--- /dev/null
+++ b/src/lib/barrier/IClipboard.h
@@ -0,0 +1,169 @@
+/*
+ * barrier -- 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"
+#include "base/EventTypes.h"
+#include "common/IInterface.h"
+
+//! Clipboard interface
+/*!
+This interface defines the methods common to all clipboards.
+*/
+class IClipboard : public IInterface {
+public:
+ //! Timestamp type
+ /*!
+ Timestamp type. Timestamps are in milliseconds from some
+ arbitrary starting time. Timestamps will wrap around to 0
+ after about 49 3/4 days.
+ */
+ typedef UInt32 Time;
+
+ //! Clipboard formats
+ /*!
+ The list of known clipboard formats. kNumFormats must be last and
+ formats must be sequential starting from zero. Clipboard data set
+ via add() and retrieved via get() must be in one of these formats.
+ Platform dependent clipboard subclasses can and should present any
+ suitable formats derivable from these formats.
+
+ \c kText is a text format encoded in UTF-8. Newlines are LF (not
+ CR or LF/CR).
+
+ \c kBitmap is an image format. The data is a BMP file without the
+ 14 byte header (i.e. starting at the INFOHEADER) and with the image
+ data immediately following the 40 byte INFOHEADER.
+
+ \c kHTML is a text format encoded in UTF-8 and containing a valid
+ HTML fragment (but not necessarily a complete HTML document).
+ Newlines are LF.
+ */
+ enum EFormat {
+ kText, //!< Text format, UTF-8, newline is LF
+ kHTML, //!< HTML format, HTML fragment, UTF-8, newline is LF
+ kBitmap, //!< Bitmap format, BMP 24/32bpp, BI_RGB
+ kNumFormats //!< The number of clipboard formats
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! Empty clipboard
+ /*!
+ Take ownership of the clipboard and clear all data from it.
+ This must be called between a successful open() and close().
+ Return false if the clipboard ownership could not be taken;
+ the clipboard should not be emptied in this case.
+ */
+ virtual bool empty() = 0;
+
+ //! Add data
+ /*!
+ Add data in the given format to the clipboard. May only be
+ called after a successful empty().
+ */
+ virtual void add(EFormat, const String& data) = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Open clipboard
+ /*!
+ Open the clipboard. Return true iff the clipboard could be
+ opened. If open() returns true then the client must call
+ close() at some later time; if it returns false then close()
+ must not be called. \c time should be the current time or
+ a time in the past when the open should effectively have taken
+ place.
+ */
+ virtual bool open(Time time) const = 0;
+
+ //! Close clipboard
+ /*!
+ Close the clipboard. close() must match a preceding successful
+ open(). This signals that the clipboard has been filled with
+ all the necessary data or all data has been read. It does not
+ mean the clipboard ownership should be released (if it was
+ taken).
+ */
+ virtual void close() const = 0;
+
+ //! Get time
+ /*!
+ Return the timestamp passed to the last successful open().
+ */
+ virtual Time getTime() const = 0;
+
+ //! Check for data
+ /*!
+ Return true iff the clipboard contains data in the given
+ format. Must be called between a successful open() and close().
+ */
+ virtual bool has(EFormat) const = 0;
+
+ //! Get data
+ /*!
+ Return the data in the given format. Returns the empty string
+ if there is no data in that format. Must be called between
+ a successful open() and close().
+ */
+ virtual String get(EFormat) const = 0;
+
+ //! Marshall clipboard data
+ /*!
+ Merge \p clipboard's data into a single buffer that can be later
+ unmarshalled to restore the clipboard and return the buffer.
+ */
+ static String marshall(const IClipboard* clipboard);
+
+ //! Unmarshall clipboard data
+ /*!
+ Extract marshalled clipboard data and store it in \p clipboard.
+ Sets the clipboard time to \c time.
+ */
+ static void unmarshall(IClipboard* clipboard,
+ const String& data, Time time);
+
+ //! Copy clipboard
+ /*!
+ Transfers all the data in one clipboard to another. The
+ clipboards can be of any concrete clipboard type (and
+ they don't have to be the same type). This also sets
+ the destination clipboard's timestamp to source clipboard's
+ timestamp. Returns true iff the copy succeeded.
+ */
+ static bool copy(IClipboard* dst, const IClipboard* src);
+
+ //! Copy clipboard
+ /*!
+ Transfers all the data in one clipboard to another. The
+ clipboards can be of any concrete clipboard type (and they
+ don't have to be the same type). This also sets the
+ timestamp to \c time. Returns true iff the copy succeeded.
+ */
+ static bool copy(IClipboard* dst, const IClipboard* src, Time);
+
+ //@}
+
+private:
+ static UInt32 readUInt32(const char*);
+ static void writeUInt32(String*, UInt32);
+};
diff --git a/src/lib/barrier/IKeyState.cpp b/src/lib/barrier/IKeyState.cpp
new file mode 100644
index 0000000..5d1114c
--- /dev/null
+++ b/src/lib/barrier/IKeyState.cpp
@@ -0,0 +1,161 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/IKeyState.h"
+#include "base/EventQueue.h"
+
+#include <cstring>
+#include <cstdlib>
+
+//
+// IKeyState
+//
+
+IKeyState::IKeyState(IEventQueue* events)
+{
+}
+
+//
+// IKeyState::KeyInfo
+//
+
+IKeyState::KeyInfo*
+IKeyState::KeyInfo::alloc(KeyID id,
+ KeyModifierMask mask, KeyButton button, SInt32 count)
+{
+ KeyInfo* info = (KeyInfo*)malloc(sizeof(KeyInfo));
+ info->m_key = id;
+ info->m_mask = mask;
+ info->m_button = button;
+ info->m_count = count;
+ info->m_screens = NULL;
+ info->m_screensBuffer[0] = '\0';
+ return info;
+}
+
+IKeyState::KeyInfo*
+IKeyState::KeyInfo::alloc(KeyID id,
+ KeyModifierMask mask, KeyButton button, SInt32 count,
+ const std::set<String>& destinations)
+{
+ String screens = join(destinations);
+
+ // build structure
+ KeyInfo* info = (KeyInfo*)malloc(sizeof(KeyInfo) + screens.size());
+ info->m_key = id;
+ info->m_mask = mask;
+ info->m_button = button;
+ info->m_count = count;
+ info->m_screens = info->m_screensBuffer;
+ strcpy(info->m_screensBuffer, screens.c_str());
+ return info;
+}
+
+IKeyState::KeyInfo*
+IKeyState::KeyInfo::alloc(const KeyInfo& x)
+{
+ KeyInfo* info = (KeyInfo*)malloc(sizeof(KeyInfo) +
+ strlen(x.m_screensBuffer));
+ info->m_key = x.m_key;
+ info->m_mask = x.m_mask;
+ info->m_button = x.m_button;
+ info->m_count = x.m_count;
+ info->m_screens = x.m_screens ? info->m_screensBuffer : NULL;
+ strcpy(info->m_screensBuffer, x.m_screensBuffer);
+ return info;
+}
+
+bool
+IKeyState::KeyInfo::isDefault(const char* screens)
+{
+ return (screens == NULL || screens[0] == '\0');
+}
+
+bool
+IKeyState::KeyInfo::contains(const char* screens, const String& name)
+{
+ // special cases
+ if (isDefault(screens)) {
+ return false;
+ }
+ if (screens[0] == '*') {
+ return true;
+ }
+
+ // search
+ String match;
+ match.reserve(name.size() + 2);
+ match += ":";
+ match += name;
+ match += ":";
+ return (strstr(screens, match.c_str()) != NULL);
+}
+
+bool
+IKeyState::KeyInfo::equal(const KeyInfo* a, const KeyInfo* b)
+{
+ return (a->m_key == b->m_key &&
+ a->m_mask == b->m_mask &&
+ a->m_button == b->m_button &&
+ a->m_count == b->m_count &&
+ strcmp(a->m_screensBuffer, b->m_screensBuffer) == 0);
+}
+
+String
+IKeyState::KeyInfo::join(const std::set<String>& destinations)
+{
+ // collect destinations into a string. names are surrounded by ':'
+ // which makes searching easy. the string is empty if there are no
+ // destinations and "*" means all destinations.
+ String screens;
+ for (std::set<String>::const_iterator i = destinations.begin();
+ i != destinations.end(); ++i) {
+ if (*i == "*") {
+ screens = "*";
+ break;
+ }
+ else {
+ if (screens.empty()) {
+ screens = ":";
+ }
+ screens += *i;
+ screens += ":";
+ }
+ }
+ return screens;
+}
+
+void
+IKeyState::KeyInfo::split(const char* screens, std::set<String>& dst)
+{
+ dst.clear();
+ if (isDefault(screens)) {
+ return;
+ }
+ if (screens[0] == '*') {
+ dst.insert("*");
+ return;
+ }
+
+ const char* i = screens + 1;
+ while (*i != '\0') {
+ const char* j = strchr(i, ':');
+ dst.insert(String(i, j - i));
+ i = j + 1;
+ }
+}
diff --git a/src/lib/barrier/IKeyState.h b/src/lib/barrier/IKeyState.h
new file mode 100644
index 0000000..b9d4706
--- /dev/null
+++ b/src/lib/barrier/IKeyState.h
@@ -0,0 +1,174 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/key_types.h"
+#include "base/Event.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "base/EventTypes.h"
+#include "common/stdset.h"
+#include "common/IInterface.h"
+
+//! Key state interface
+/*!
+This interface provides access to set and query the keyboard state and
+to synthesize key events.
+*/
+class IKeyState : public IInterface {
+public:
+ IKeyState(IEventQueue* events);
+
+ enum {
+ kNumButtons = 0x200
+ };
+
+ //! Key event data
+ class KeyInfo {
+ public:
+ static KeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count);
+ static KeyInfo* alloc(KeyID, KeyModifierMask, KeyButton, SInt32 count,
+ const std::set<String>& destinations);
+ static KeyInfo* alloc(const KeyInfo&);
+
+ static bool isDefault(const char* screens);
+ static bool contains(const char* screens, const String& name);
+ static bool equal(const KeyInfo*, const KeyInfo*);
+ static String join(const std::set<String>& destinations);
+ static void split(const char* screens, std::set<String>&);
+
+ public:
+ KeyID m_key;
+ KeyModifierMask m_mask;
+ KeyButton m_button;
+ SInt32 m_count;
+ char* m_screens;
+ char m_screensBuffer[1];
+ };
+
+ typedef std::set<KeyButton> KeyButtonSet;
+
+ //! @name manipulators
+ //@{
+
+ //! Update the keyboard map
+ /*!
+ Causes the key state to get updated to reflect the current keyboard
+ mapping.
+ */
+ virtual void updateKeyMap() = 0;
+
+ //! Update the key state
+ /*!
+ Causes the key state to get updated to reflect the physical keyboard
+ state.
+ */
+ virtual void updateKeyState() = 0;
+
+ //! Set half-duplex mask
+ /*!
+ Sets which modifier toggle keys are half-duplex. A half-duplex
+ toggle key doesn't report a key release when toggled on and
+ doesn't report a key press when toggled off.
+ */
+ virtual void setHalfDuplexMask(KeyModifierMask) = 0;
+
+ //! Fake a key press
+ /*!
+ Synthesizes a key press event and updates the key state.
+ */
+ virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button) = 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;
+
+ //! Fake a key release
+ /*!
+ Synthesizes a key release event and updates the key state.
+ */
+ virtual bool fakeKeyUp(KeyButton button) = 0;
+
+ //! Fake key releases for all fake pressed keys
+ /*!
+ Synthesizes a key release event for every key that is synthetically
+ pressed and updates the key state.
+ */
+ virtual void fakeAllKeysUp() = 0;
+
+ //! Fake ctrl+alt+del
+ /*!
+ Synthesize a press of ctrl+alt+del. Return true if processing is
+ complete and false if normal key processing should continue.
+ */
+ virtual bool fakeCtrlAltDel() = 0;
+
+ //! Fake a media key
+ /*!
+ Synthesizes a media key down and up. Only Mac would implement this by
+ use cocoa appkit framework.
+ */
+ virtual bool fakeMediaKey(KeyID id) = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test if key is pressed
+ /*!
+ Returns true iff the given key is down. Half-duplex toggles
+ always return false.
+ */
+ virtual bool isKeyDown(KeyButton) const = 0;
+
+ //! Get the active modifiers
+ /*!
+ Returns the modifiers that are currently active according to our
+ shadowed state.
+ */
+ virtual KeyModifierMask
+ getActiveModifiers() const = 0;
+
+ //! Get the active modifiers from OS
+ /*!
+ Returns the modifiers that are currently active according to the
+ operating system.
+ */
+ virtual KeyModifierMask
+ pollActiveModifiers() const = 0;
+
+ //! Get the active keyboard layout from OS
+ /*!
+ Returns the active keyboard layout according to the operating system.
+ */
+ virtual SInt32 pollActiveGroup() const = 0;
+
+ //! Get the keys currently pressed from OS
+ /*!
+ Adds any keys that are currently pressed according to the operating
+ system to \p pressedKeys.
+ */
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0;
+
+ //@}
+};
diff --git a/src/lib/barrier/INode.h b/src/lib/barrier/INode.h
new file mode 100644
index 0000000..2e78f7c
--- /dev/null
+++ b/src/lib/barrier/INode.h
@@ -0,0 +1,25 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+
+class INode : IInterface {
+
+};
diff --git a/src/lib/barrier/IPlatformScreen.cpp b/src/lib/barrier/IPlatformScreen.cpp
new file mode 100644
index 0000000..d1d9f78
--- /dev/null
+++ b/src/lib/barrier/IPlatformScreen.cpp
@@ -0,0 +1,24 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2016 Symless.
+ *
+ * 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 COPYING 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 "barrier/IPlatformScreen.h"
+
+bool
+IPlatformScreen::fakeMediaKey(KeyID id)
+{
+ return false;
+}
diff --git a/src/lib/barrier/IPlatformScreen.h b/src/lib/barrier/IPlatformScreen.h
new file mode 100644
index 0000000..440e218
--- /dev/null
+++ b/src/lib/barrier/IPlatformScreen.h
@@ -0,0 +1,227 @@
+/*
+ * barrier -- 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 "barrier/DragInformation.h"
+#include "barrier/clipboard_types.h"
+#include "barrier/IScreen.h"
+#include "barrier/IPrimaryScreen.h"
+#include "barrier/ISecondaryScreen.h"
+#include "barrier/IKeyState.h"
+#include "barrier/option_types.h"
+
+class IClipboard;
+
+//! Screen interface
+/*!
+This interface defines the methods common to all platform dependent
+screen implementations that are used by both primary and secondary
+screens.
+*/
+class IPlatformScreen : public IScreen,
+ public IPrimaryScreen, public ISecondaryScreen,
+ public IKeyState {
+public:
+ //! @name manipulators
+ //@{
+
+ IPlatformScreen(IEventQueue* events) : IKeyState(events) { }
+
+ //! Enable screen
+ /*!
+ Enable the screen, preparing it to report system and user events.
+ For a secondary screen it also means preparing to synthesize events
+ and hiding the cursor.
+ */
+ virtual void enable() = 0;
+
+ //! Disable screen
+ /*!
+ Undoes the operations in enable() and events should no longer
+ be reported.
+ */
+ virtual void disable() = 0;
+
+ //! Enter screen
+ /*!
+ Called when the user navigates to this screen.
+ */
+ virtual void enter() = 0;
+
+ //! Leave screen
+ /*!
+ Called when the user navigates off the screen. Returns true on
+ success, false on failure. A typical reason for failure is being
+ unable to install the keyboard and mouse snoopers on a primary
+ screen. Secondary screens should not fail.
+ */
+ virtual bool leave() = 0;
+
+ //! Set clipboard
+ /*!
+ Set the contents of the system clipboard indicated by \c id.
+ */
+ virtual bool setClipboard(ClipboardID id, const IClipboard*) = 0;
+
+ //! Check clipboard owner
+ /*!
+ Check ownership of all clipboards and post grab events for any that
+ have changed. This is used as a backup in case the system doesn't
+ reliably report clipboard ownership changes.
+ */
+ virtual void checkClipboards() = 0;
+
+ //! Open screen saver
+ /*!
+ Open the screen saver. If \c notify is true then this object must
+ send events when the screen saver activates or deactivates until
+ \c closeScreensaver() is called. If \c notify is false then the
+ screen saver is disabled and restored on \c closeScreensaver().
+ */
+ virtual void openScreensaver(bool notify) = 0;
+
+ //! Close screen saver
+ /*!
+ // Close the screen saver. Stop reporting screen saver activation
+ and deactivation and, if the screen saver was disabled by
+ openScreensaver(), enable the screen saver.
+ */
+ virtual void closeScreensaver() = 0;
+
+ //! Activate/deactivate screen saver
+ /*!
+ Forcibly activate the screen saver if \c activate is true otherwise
+ forcibly deactivate it.
+ */
+ virtual void screensaver(bool activate) = 0;
+
+ //! Notify of options changes
+ /*!
+ Reset all options to their default values.
+ */
+ virtual void resetOptions() = 0;
+
+ //! Notify of options changes
+ /*!
+ Set options to given values. Ignore unknown options and don't
+ modify options that aren't given in \c options.
+ */
+ virtual void setOptions(const OptionsList& options) = 0;
+
+ //! Set clipboard sequence number
+ /*!
+ Sets the sequence number to use in subsequent clipboard events.
+ */
+ virtual void setSequenceNumber(UInt32) = 0;
+
+ //! Change dragging status
+ virtual void setDraggingStarted(bool started) = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test if is primary screen
+ /*!
+ Return true iff this screen is a primary screen.
+ */
+ virtual bool isPrimary() const = 0;
+
+ //@}
+
+ // IScreen overrides
+ virtual void* getEventTarget() const = 0;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const = 0;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
+
+ // IPrimaryScreen overrides
+ virtual void reconfigure(UInt32 activeSides) = 0;
+ virtual void warpCursor(SInt32 x, SInt32 y) = 0;
+ virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask) = 0;
+ virtual void unregisterHotKey(UInt32 id) = 0;
+ virtual void fakeInputBegin() = 0;
+ virtual void fakeInputEnd() = 0;
+ virtual SInt32 getJumpZoneSize() const = 0;
+ virtual bool isAnyMouseButtonDown(UInt32& buttonID) const = 0;
+ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
+
+ // ISecondaryScreen overrides
+ virtual void fakeMouseButton(ButtonID id, bool press) = 0;
+ virtual void fakeMouseMove(SInt32 x, SInt32 y) = 0;
+ virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0;
+ virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0;
+
+ // IKeyState overrides
+ virtual void updateKeyMap() = 0;
+ virtual void updateKeyState() = 0;
+ virtual void setHalfDuplexMask(KeyModifierMask) = 0;
+ virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button) = 0;
+ virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button) = 0;
+ virtual bool fakeKeyUp(KeyButton button) = 0;
+ virtual void fakeAllKeysUp() = 0;
+ virtual bool fakeCtrlAltDel() = 0;
+ virtual bool fakeMediaKey(KeyID id);
+ virtual bool isKeyDown(KeyButton) const = 0;
+ virtual KeyModifierMask
+ getActiveModifiers() const = 0;
+ virtual KeyModifierMask
+ pollActiveModifiers() const = 0;
+ virtual SInt32 pollActiveGroup() const = 0;
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0;
+
+ virtual String& getDraggingFilename() = 0;
+ virtual void clearDraggingFilename() = 0;
+ virtual bool isDraggingStarted() = 0;
+ virtual bool isFakeDraggingStarted() = 0;
+
+ virtual void fakeDraggingFiles(DragFileList fileList) = 0;
+ virtual const String&
+ getDropTarget() const = 0;
+
+protected:
+ //! Handle system event
+ /*!
+ A platform screen is expected to install a handler for system
+ events in its c'tor like so:
+ \code
+ m_events->adoptHandler(Event::kSystem,
+ m_events->getSystemTarget(),
+ new TMethodEventJob<CXXXPlatformScreen>(this,
+ &CXXXPlatformScreen::handleSystemEvent));
+ \endcode
+ It should remove the handler in its d'tor. Override the
+ \c handleSystemEvent() method to process system events.
+ It should post the events \c IScreen as appropriate.
+
+ A primary screen has further responsibilities. It should post
+ the events in \c IPrimaryScreen as appropriate. It should also
+ call \c onKey() on its \c KeyState whenever a key is pressed
+ or released (but not for key repeats). And it should call
+ \c updateKeyMap() on its \c KeyState if necessary when the keyboard
+ mapping changes.
+
+ The target of all events should be the value returned by
+ \c getEventTarget().
+ */
+ virtual void handleSystemEvent(const Event& event, void*) = 0;
+};
diff --git a/src/lib/barrier/IPrimaryScreen.cpp b/src/lib/barrier/IPrimaryScreen.cpp
new file mode 100644
index 0000000..4954e4f
--- /dev/null
+++ b/src/lib/barrier/IPrimaryScreen.cpp
@@ -0,0 +1,91 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/IPrimaryScreen.h"
+#include "base/EventQueue.h"
+
+#include <cstdlib>
+
+//
+// IPrimaryScreen::ButtonInfo
+//
+
+IPrimaryScreen::ButtonInfo*
+IPrimaryScreen::ButtonInfo::alloc(ButtonID id, KeyModifierMask mask)
+{
+ ButtonInfo* info = (ButtonInfo*)malloc(sizeof(ButtonInfo));
+ info->m_button = id;
+ info->m_mask = mask;
+ return info;
+}
+
+IPrimaryScreen::ButtonInfo*
+IPrimaryScreen::ButtonInfo::alloc(const ButtonInfo& x)
+{
+ ButtonInfo* info = (ButtonInfo*)malloc(sizeof(ButtonInfo));
+ info->m_button = x.m_button;
+ info->m_mask = x.m_mask;
+ return info;
+}
+
+bool
+IPrimaryScreen::ButtonInfo::equal(const ButtonInfo* a, const ButtonInfo* b)
+{
+ return (a->m_button == b->m_button && a->m_mask == b->m_mask);
+}
+
+
+//
+// IPrimaryScreen::MotionInfo
+//
+
+IPrimaryScreen::MotionInfo*
+IPrimaryScreen::MotionInfo::alloc(SInt32 x, SInt32 y)
+{
+ MotionInfo* info = (MotionInfo*)malloc(sizeof(MotionInfo));
+ info->m_x = x;
+ info->m_y = y;
+ return info;
+}
+
+
+//
+// IPrimaryScreen::WheelInfo
+//
+
+IPrimaryScreen::WheelInfo*
+IPrimaryScreen::WheelInfo::alloc(SInt32 xDelta, SInt32 yDelta)
+{
+ WheelInfo* info = (WheelInfo*)malloc(sizeof(WheelInfo));
+ info->m_xDelta = xDelta;
+ info->m_yDelta = yDelta;
+ return info;
+}
+
+
+//
+// IPrimaryScreen::HotKeyInfo
+//
+
+IPrimaryScreen::HotKeyInfo*
+IPrimaryScreen::HotKeyInfo::alloc(UInt32 id)
+{
+ HotKeyInfo* info = (HotKeyInfo*)malloc(sizeof(HotKeyInfo));
+ info->m_id = id;
+ return info;
+}
diff --git a/src/lib/barrier/IPrimaryScreen.h b/src/lib/barrier/IPrimaryScreen.h
new file mode 100644
index 0000000..7f3fa9c
--- /dev/null
+++ b/src/lib/barrier/IPrimaryScreen.h
@@ -0,0 +1,165 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/key_types.h"
+#include "barrier/mouse_types.h"
+#include "base/Event.h"
+#include "base/EventTypes.h"
+#include "common/IInterface.h"
+
+//! Primary screen interface
+/*!
+This interface defines the methods common to all platform dependent
+primary screen implementations.
+*/
+class IPrimaryScreen : public IInterface {
+public:
+ //! Button event data
+ class ButtonInfo {
+ public:
+ static ButtonInfo* alloc(ButtonID, KeyModifierMask);
+ static ButtonInfo* alloc(const ButtonInfo&);
+
+ static bool equal(const ButtonInfo*, const ButtonInfo*);
+
+ public:
+ ButtonID m_button;
+ KeyModifierMask m_mask;
+ };
+ //! Motion event data
+ class MotionInfo {
+ public:
+ static MotionInfo* alloc(SInt32 x, SInt32 y);
+
+ public:
+ SInt32 m_x;
+ SInt32 m_y;
+ };
+ //! Wheel motion event data
+ class WheelInfo {
+ public:
+ static WheelInfo* alloc(SInt32 xDelta, SInt32 yDelta);
+
+ public:
+ SInt32 m_xDelta;
+ SInt32 m_yDelta;
+ };
+ //! Hot key event data
+ class HotKeyInfo {
+ public:
+ static HotKeyInfo* alloc(UInt32 id);
+
+ public:
+ UInt32 m_id;
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! Update configuration
+ /*!
+ This is called when the configuration has changed. \c activeSides
+ is a bitmask of EDirectionMask indicating which sides of the
+ primary screen are linked to clients. Override to handle the
+ possible change in jump zones.
+ */
+ virtual void reconfigure(UInt32 activeSides) = 0;
+
+ //! Warp cursor
+ /*!
+ Warp the cursor to the absolute coordinates \c x,y. Also
+ discard input events up to and including the warp before
+ returning.
+ */
+ virtual void warpCursor(SInt32 x, SInt32 y) = 0;
+
+ //! Register a system hotkey
+ /*!
+ Registers a system-wide hotkey. The screen should arrange for an event
+ to be delivered to itself when the hot key is pressed or released. When
+ that happens the screen should post a \c getHotKeyDownEvent() or
+ \c getHotKeyUpEvent(), respectively. The hot key is key \p key with
+ exactly the modifiers \p mask. Returns 0 on failure otherwise an id
+ that can be used to unregister the hotkey.
+
+ A hot key is a set of modifiers and a key, which may itself be a modifier.
+ The hot key is pressed when the hot key's modifiers and only those
+ modifiers are logically down (active) and the key is pressed. The hot
+ key is released when the key is released, regardless of the modifiers.
+
+ The hot key event should be generated no matter what window or application
+ has the focus. No other window or application should receive the key
+ press or release events (they can and should see the modifier key events).
+ When the key is a modifier, it's acceptable to allow the user to press
+ the modifiers in any order or to require the user to press the given key
+ last.
+ */
+ virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask) = 0;
+
+ //! Unregister a system hotkey
+ /*!
+ Unregisters a previously registered hot key.
+ */
+ virtual void unregisterHotKey(UInt32 id) = 0;
+
+ //! Prepare to synthesize input on primary screen
+ /*!
+ Prepares the primary screen to receive synthesized input. We do not
+ want to receive this synthesized input as user input so this method
+ ensures that we ignore it. Calls to \c fakeInputBegin() may not be
+ nested.
+ */
+ virtual void fakeInputBegin() = 0;
+
+ //! Done synthesizing input on primary screen
+ /*!
+ Undoes whatever \c fakeInputBegin() did.
+ */
+ virtual void fakeInputEnd() = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get jump zone size
+ /*!
+ Return the jump zone size, the size of the regions on the edges of
+ the screen that cause the cursor to jump to another screen.
+ */
+ virtual SInt32 getJumpZoneSize() const = 0;
+
+ //! Test if mouse is pressed
+ /*!
+ Return true if any mouse button is currently pressed. Ideally,
+ "current" means up to the last processed event but it can mean
+ the current physical mouse button state.
+ */
+ virtual bool isAnyMouseButtonDown(UInt32& buttonID) const = 0;
+
+ //! Get cursor center position
+ /*!
+ Return the cursor center position which is where we park the
+ cursor to compute cursor motion deltas and should be far from
+ the edges of the screen, typically the center.
+ */
+ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
+
+ //@}
+};
diff --git a/src/lib/barrier/IScreen.h b/src/lib/barrier/IScreen.h
new file mode 100644
index 0000000..47d6578
--- /dev/null
+++ b/src/lib/barrier/IScreen.h
@@ -0,0 +1,71 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/clipboard_types.h"
+#include "base/Event.h"
+#include "base/EventTypes.h"
+#include "common/IInterface.h"
+
+class IClipboard;
+
+//! Screen interface
+/*!
+This interface defines the methods common to all screens.
+*/
+class IScreen : public IInterface {
+public:
+ struct ClipboardInfo {
+ public:
+ ClipboardID m_id;
+ UInt32 m_sequenceNumber;
+ };
+
+ //! @name accessors
+ //@{
+
+ //! Get event target
+ /*!
+ Returns the target used for events created by this object.
+ */
+ virtual void* getEventTarget() const = 0;
+
+ //! Get clipboard
+ /*!
+ Save the contents of the clipboard indicated by \c id and return
+ true iff successful.
+ */
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
+
+ //! Get screen shape
+ /*!
+ Return the position of the upper-left corner of the screen in \c x and
+ \c y and the size of the screen in \c width and \c height.
+ */
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const = 0;
+
+ //! Get cursor position
+ /*!
+ Return the current position of the cursor in \c x and \c y.
+ */
+ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
+
+ //@}
+};
diff --git a/src/lib/barrier/IScreenSaver.h b/src/lib/barrier/IScreenSaver.h
new file mode 100644
index 0000000..fc21ac5
--- /dev/null
+++ b/src/lib/barrier/IScreenSaver.h
@@ -0,0 +1,75 @@
+/*
+ * barrier -- 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/Event.h"
+#include "common/IInterface.h"
+
+//! Screen saver interface
+/*!
+This interface defines the methods common to all screen savers.
+*/
+class IScreenSaver : public IInterface {
+public:
+ // note -- the c'tor/d'tor must *not* enable/disable the screen saver
+
+ //! @name manipulators
+ //@{
+
+ //! Enable screen saver
+ /*!
+ Enable the screen saver, restoring the screen saver settings to
+ what they were when disable() was previously called. If disable()
+ wasn't previously called then it should keep the current settings
+ or use reasonable defaults.
+ */
+ virtual void enable() = 0;
+
+ //! Disable screen saver
+ /*!
+ Disable the screen saver, saving the old settings for the next
+ call to enable().
+ */
+ virtual void disable() = 0;
+
+ //! Activate screen saver
+ /*!
+ Activate (i.e. show) the screen saver.
+ */
+ virtual void activate() = 0;
+
+ //! Deactivate screen saver
+ /*!
+ Deactivate (i.e. hide) the screen saver, reseting the screen saver
+ timer.
+ */
+ virtual void deactivate() = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test if screen saver on
+ /*!
+ Returns true iff the screen saver is currently active (showing).
+ */
+ virtual bool isActive() const = 0;
+
+ //@}
+};
diff --git a/src/lib/barrier/ISecondaryScreen.h b/src/lib/barrier/ISecondaryScreen.h
new file mode 100644
index 0000000..527ca2e
--- /dev/null
+++ b/src/lib/barrier/ISecondaryScreen.h
@@ -0,0 +1,61 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/mouse_types.h"
+#include "base/Event.h"
+#include "base/EventTypes.h"
+#include "common/IInterface.h"
+
+//! Secondary screen interface
+/*!
+This interface defines the methods common to all platform dependent
+secondary screen implementations.
+*/
+class ISecondaryScreen : public IInterface {
+public:
+ //! @name accessors
+ //@{
+
+ //! Fake mouse press/release
+ /*!
+ Synthesize a press or release of mouse button \c id.
+ */
+ virtual void fakeMouseButton(ButtonID id, bool press) = 0;
+
+ //! Fake mouse move
+ /*!
+ Synthesize a mouse move to the absolute coordinates \c x,y.
+ */
+ virtual void fakeMouseMove(SInt32 x, SInt32 y) = 0;
+
+ //! Fake mouse move
+ /*!
+ Synthesize a mouse move to the relative coordinates \c dx,dy.
+ */
+ virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0;
+
+ //! Fake mouse wheel
+ /*!
+ Synthesize a mouse wheel event of amount \c xDelta and \c yDelta.
+ */
+ virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0;
+
+ //@}
+};
diff --git a/src/lib/barrier/KeyMap.cpp b/src/lib/barrier/KeyMap.cpp
new file mode 100644
index 0000000..fd68204
--- /dev/null
+++ b/src/lib/barrier/KeyMap.cpp
@@ -0,0 +1,1344 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2005 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 "barrier/KeyMap.h"
+#include "barrier/key_types.h"
+#include "base/Log.h"
+
+#include <assert.h>
+#include <cctype>
+#include <cstdlib>
+
+namespace barrier {
+
+KeyMap::NameToKeyMap* KeyMap::s_nameToKeyMap = NULL;
+KeyMap::NameToModifierMap* KeyMap::s_nameToModifierMap = NULL;
+KeyMap::KeyToNameMap* KeyMap::s_keyToNameMap = NULL;
+KeyMap::ModifierToNameMap* KeyMap::s_modifierToNameMap = NULL;
+
+KeyMap::KeyMap() :
+ m_numGroups(0),
+ m_composeAcrossGroups(false)
+{
+ m_modifierKeyItem.m_id = kKeyNone;
+ m_modifierKeyItem.m_group = 0;
+ m_modifierKeyItem.m_button = 0;
+ m_modifierKeyItem.m_required = 0;
+ m_modifierKeyItem.m_sensitive = 0;
+ m_modifierKeyItem.m_generates = 0;
+ m_modifierKeyItem.m_dead = false;
+ m_modifierKeyItem.m_lock = false;
+ m_modifierKeyItem.m_client = 0;
+}
+
+KeyMap::~KeyMap()
+{
+ // do nothing
+}
+
+void
+KeyMap::swap(KeyMap& x)
+{
+ m_keyIDMap.swap(x.m_keyIDMap);
+ m_modifierKeys.swap(x.m_modifierKeys);
+ m_halfDuplex.swap(x.m_halfDuplex);
+ m_halfDuplexMods.swap(x.m_halfDuplexMods);
+ SInt32 tmp1 = m_numGroups;
+ m_numGroups = x.m_numGroups;
+ x.m_numGroups = tmp1;
+ bool tmp2 = m_composeAcrossGroups;
+ m_composeAcrossGroups = x.m_composeAcrossGroups;
+ x.m_composeAcrossGroups = tmp2;
+}
+
+void
+KeyMap::addKeyEntry(const KeyItem& item)
+{
+ // ignore kKeyNone
+ if (item.m_id == kKeyNone) {
+ return;
+ }
+
+ // resize number of groups for key
+ SInt32 numGroups = item.m_group + 1;
+ if (getNumGroups() > numGroups) {
+ numGroups = getNumGroups();
+ }
+ KeyGroupTable& groupTable = m_keyIDMap[item.m_id];
+ if (groupTable.size() < static_cast<size_t>(numGroups)) {
+ groupTable.resize(numGroups);
+ }
+
+ // make a list from the item
+ KeyItemList items;
+ items.push_back(item);
+
+ // set group and dead key flag on the item
+ KeyItem& newItem = items.back();
+ newItem.m_dead = isDeadKey(item.m_id);
+
+ // mask the required bits with the sensitive bits
+ newItem.m_required &= newItem.m_sensitive;
+
+ // see if we already have this item; just return if so
+ KeyEntryList& entries = groupTable[item.m_group];
+ for (size_t i = 0, n = entries.size(); i < n; ++i) {
+ if (entries[i].size() == 1 && newItem == entries[i][0]) {
+ return;
+ }
+ }
+
+ // add item list
+ entries.push_back(items);
+ LOG((CLOG_DEBUG5 "add key: %04x %d %03x %04x (%04x %04x %04x)%s", newItem.m_id, newItem.m_group, newItem.m_button, newItem.m_client, newItem.m_required, newItem.m_sensitive, newItem.m_generates, newItem.m_dead ? " dead" : ""));
+}
+
+void
+KeyMap::addKeyAliasEntry(KeyID targetID, SInt32 group,
+ KeyModifierMask targetRequired,
+ KeyModifierMask targetSensitive,
+ KeyID sourceID,
+ KeyModifierMask sourceRequired,
+ KeyModifierMask sourceSensitive)
+{
+ // if we can already generate the target as desired then we're done.
+ if (findCompatibleKey(targetID, group, targetRequired,
+ targetSensitive) != NULL) {
+ return;
+ }
+
+ // find a compatible source, preferably in the same group
+ for (SInt32 gd = 0, n = getNumGroups(); gd < n; ++gd) {
+ SInt32 eg = getEffectiveGroup(group, gd);
+ const KeyItemList* sourceEntry =
+ findCompatibleKey(sourceID, eg,
+ sourceRequired, sourceSensitive);
+ if (sourceEntry != NULL && sourceEntry->size() == 1) {
+ KeyMap::KeyItem targetItem = sourceEntry->back();
+ targetItem.m_id = targetID;
+ targetItem.m_group = eg;
+ addKeyEntry(targetItem);
+ break;
+ }
+ }
+}
+
+bool
+KeyMap::addKeyCombinationEntry(KeyID id, SInt32 group,
+ const KeyID* keys, UInt32 numKeys)
+{
+ // disallow kKeyNone
+ if (id == kKeyNone) {
+ return false;
+ }
+
+ SInt32 numGroups = group + 1;
+ if (getNumGroups() > numGroups) {
+ numGroups = getNumGroups();
+ }
+ KeyGroupTable& groupTable = m_keyIDMap[id];
+ if (groupTable.size() < static_cast<size_t>(numGroups)) {
+ groupTable.resize(numGroups);
+ }
+ if (!groupTable[group].empty()) {
+ // key is already in the table
+ return false;
+ }
+
+ // convert to buttons
+ KeyItemList items;
+ for (UInt32 i = 0; i < numKeys; ++i) {
+ KeyIDMap::const_iterator gtIndex = m_keyIDMap.find(keys[i]);
+ if (gtIndex == m_keyIDMap.end()) {
+ return false;
+ }
+ const KeyGroupTable& groupTable = gtIndex->second;
+
+ // if we allow group switching during composition then search all
+ // groups for keys, otherwise search just the given group.
+ SInt32 n = 1;
+ if (m_composeAcrossGroups) {
+ n = (SInt32)groupTable.size();
+ }
+
+ bool found = false;
+ for (SInt32 gd = 0; gd < n && !found; ++gd) {
+ SInt32 eg = (group + gd) % getNumGroups();
+ const KeyEntryList& entries = groupTable[eg];
+ for (size_t j = 0; j < entries.size(); ++j) {
+ if (entries[j].size() == 1) {
+ found = true;
+ items.push_back(entries[j][0]);
+ break;
+ }
+ }
+ }
+ if (!found) {
+ // required key is not in keyboard group
+ return false;
+ }
+ }
+
+ // add key
+ groupTable[group].push_back(items);
+ return true;
+}
+
+void
+KeyMap::allowGroupSwitchDuringCompose()
+{
+ m_composeAcrossGroups = true;
+}
+
+void
+KeyMap::addHalfDuplexButton(KeyButton button)
+{
+ m_halfDuplex.insert(button);
+}
+
+void
+KeyMap::clearHalfDuplexModifiers()
+{
+ m_halfDuplexMods.clear();
+}
+
+void
+KeyMap::addHalfDuplexModifier(KeyID key)
+{
+ m_halfDuplexMods.insert(key);
+}
+
+void
+KeyMap::finish()
+{
+ m_numGroups = findNumGroups();
+
+ // make sure every key has the same number of groups
+ for (KeyIDMap::iterator i = m_keyIDMap.begin();
+ i != m_keyIDMap.end(); ++i) {
+ i->second.resize(m_numGroups);
+ }
+
+ // compute keys that generate each modifier
+ setModifierKeys();
+}
+
+void
+KeyMap::foreachKey(ForeachKeyCallback cb, void* userData)
+{
+ for (KeyIDMap::iterator i = m_keyIDMap.begin();
+ i != m_keyIDMap.end(); ++i) {
+ KeyGroupTable& groupTable = i->second;
+ for (size_t group = 0; group < groupTable.size(); ++group) {
+ KeyEntryList& entryList = groupTable[group];
+ for (size_t j = 0; j < entryList.size(); ++j) {
+ KeyItemList& itemList = entryList[j];
+ for (size_t k = 0; k < itemList.size(); ++k) {
+ (*cb)(i->first, static_cast<SInt32>(group),
+ itemList[k], userData);
+ }
+ }
+ }
+ }
+}
+
+const KeyMap::KeyItem*
+KeyMap::mapKey(Keystrokes& keys, KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const
+{
+ LOG((CLOG_DEBUG1 "mapKey %04x (%d) with mask %04x, start state: %04x", id, id, desiredMask, currentState));
+
+ // handle group change
+ if (id == kKeyNextGroup) {
+ keys.push_back(Keystroke(1, false, false));
+ return NULL;
+ }
+ else if (id == kKeyPrevGroup) {
+ keys.push_back(Keystroke(-1, false, false));
+ return NULL;
+ }
+
+ const KeyItem* item;
+ switch (id) {
+ case kKeyShift_L:
+ case kKeyShift_R:
+ case kKeyControl_L:
+ case kKeyControl_R:
+ case kKeyAlt_L:
+ case kKeyAlt_R:
+ case kKeyMeta_L:
+ case kKeyMeta_R:
+ case kKeySuper_L:
+ case kKeySuper_R:
+ case kKeyAltGr:
+ case kKeyCapsLock:
+ case kKeyNumLock:
+ case kKeyScrollLock:
+ item = mapModifierKey(keys, id, group, activeModifiers,
+ currentState, desiredMask, isAutoRepeat);
+ break;
+
+ case kKeySetModifiers:
+ if (!keysForModifierState(0, group, activeModifiers, currentState,
+ desiredMask, desiredMask, 0, keys)) {
+ LOG((CLOG_DEBUG1 "unable to set modifiers %04x", desiredMask));
+ return NULL;
+ }
+ return &m_modifierKeyItem;
+
+ case kKeyClearModifiers:
+ if (!keysForModifierState(0, group, activeModifiers, currentState,
+ currentState & ~desiredMask,
+ desiredMask, 0, keys)) {
+ LOG((CLOG_DEBUG1 "unable to clear modifiers %04x", desiredMask));
+ return NULL;
+ }
+ return &m_modifierKeyItem;
+
+ default:
+ if (isCommand(desiredMask)) {
+ item = mapCommandKey(keys, id, group, activeModifiers,
+ currentState, desiredMask, isAutoRepeat);
+ }
+ else {
+ item = mapCharacterKey(keys, id, group, activeModifiers,
+ currentState, desiredMask, isAutoRepeat);
+ }
+ break;
+ }
+
+ if (item != NULL) {
+ LOG((CLOG_DEBUG1 "mapped to %03x, new state %04x", item->m_button, currentState));
+ }
+ return item;
+}
+
+SInt32
+KeyMap::getNumGroups() const
+{
+ return m_numGroups;
+}
+
+SInt32
+KeyMap::getEffectiveGroup(SInt32 group, SInt32 offset) const
+{
+ return (group + offset + getNumGroups()) % getNumGroups();
+}
+
+const KeyMap::KeyItemList*
+KeyMap::findCompatibleKey(KeyID id, SInt32 group,
+ KeyModifierMask required, KeyModifierMask sensitive) const
+{
+ assert(group >= 0 && group < getNumGroups());
+
+ KeyIDMap::const_iterator i = m_keyIDMap.find(id);
+ if (i == m_keyIDMap.end()) {
+ return NULL;
+ }
+
+ const KeyEntryList& entries = i->second[group];
+ for (size_t j = 0; j < entries.size(); ++j) {
+ if ((entries[j].back().m_sensitive & sensitive) == 0 ||
+ (entries[j].back().m_required & sensitive) ==
+ (required & sensitive)) {
+ return &entries[j];
+ }
+ }
+
+ return NULL;
+}
+
+bool
+KeyMap::isHalfDuplex(KeyID key, KeyButton button) const
+{
+ return (m_halfDuplex.count(button) + m_halfDuplexMods.count(key) > 0);
+}
+
+bool
+KeyMap::isCommand(KeyModifierMask mask) const
+{
+ return ((mask & getCommandModifiers()) != 0);
+}
+
+KeyModifierMask
+KeyMap::getCommandModifiers() const
+{
+ // we currently treat ctrl, alt, meta and super as command modifiers.
+ // some platforms may have a more limited set (OS X only needs Alt)
+ // but this works anyway.
+ return KeyModifierControl |
+ KeyModifierAlt |
+ KeyModifierAltGr |
+ KeyModifierMeta |
+ KeyModifierSuper;
+}
+
+void
+KeyMap::collectButtons(const ModifierToKeys& mods, ButtonToKeyMap& keys)
+{
+ keys.clear();
+ for (ModifierToKeys::const_iterator i = mods.begin();
+ i != mods.end(); ++i) {
+ keys.insert(std::make_pair(i->second.m_button, &i->second));
+ }
+}
+
+void
+KeyMap::initModifierKey(KeyItem& item)
+{
+ item.m_generates = 0;
+ item.m_lock = false;
+ switch (item.m_id) {
+ case kKeyShift_L:
+ case kKeyShift_R:
+ item.m_generates = KeyModifierShift;
+ break;
+
+ case kKeyControl_L:
+ case kKeyControl_R:
+ item.m_generates = KeyModifierControl;
+ break;
+
+ case kKeyAlt_L:
+ case kKeyAlt_R:
+ item.m_generates = KeyModifierAlt;
+ break;
+
+ case kKeyMeta_L:
+ case kKeyMeta_R:
+ item.m_generates = KeyModifierMeta;
+ break;
+
+ case kKeySuper_L:
+ case kKeySuper_R:
+ item.m_generates = KeyModifierSuper;
+ break;
+
+ case kKeyAltGr:
+ item.m_generates = KeyModifierAltGr;
+ break;
+
+ case kKeyCapsLock:
+ item.m_generates = KeyModifierCapsLock;
+ item.m_lock = true;
+ break;
+
+ case kKeyNumLock:
+ item.m_generates = KeyModifierNumLock;
+ item.m_lock = true;
+ break;
+
+ case kKeyScrollLock:
+ item.m_generates = KeyModifierScrollLock;
+ item.m_lock = true;
+ break;
+
+ default:
+ // not a modifier
+ break;
+ }
+}
+
+SInt32
+KeyMap::findNumGroups() const
+{
+ size_t max = 0;
+ for (KeyIDMap::const_iterator i = m_keyIDMap.begin();
+ i != m_keyIDMap.end(); ++i) {
+ if (i->second.size() > max) {
+ max = i->second.size();
+ }
+ }
+ return static_cast<SInt32>(max);
+}
+
+void
+KeyMap::setModifierKeys()
+{
+ m_modifierKeys.clear();
+ m_modifierKeys.resize(kKeyModifierNumBits * getNumGroups());
+ for (KeyIDMap::const_iterator i = m_keyIDMap.begin();
+ i != m_keyIDMap.end(); ++i) {
+ const KeyGroupTable& groupTable = i->second;
+ for (size_t g = 0; g < groupTable.size(); ++g) {
+ const KeyEntryList& entries = groupTable[g];
+ for (size_t j = 0; j < entries.size(); ++j) {
+ // skip multi-key sequences
+ if (entries[j].size() != 1) {
+ continue;
+ }
+
+ // skip keys that don't generate a modifier
+ const KeyItem& item = entries[j].back();
+ if (item.m_generates == 0) {
+ continue;
+ }
+
+ // add key to each indicated modifier in this group
+ for (SInt32 b = 0; b < kKeyModifierNumBits; ++b) {
+ // skip if item doesn't generate bit b
+ if (((1u << b) & item.m_generates) != 0) {
+ SInt32 mIndex = (SInt32)g * kKeyModifierNumBits + b;
+ m_modifierKeys[mIndex].push_back(&item);
+ }
+ }
+ }
+ }
+ }
+}
+
+const KeyMap::KeyItem*
+KeyMap::mapCommandKey(Keystrokes& keys, KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const
+{
+ static const KeyModifierMask s_overrideModifiers = 0xffffu;
+
+ // find KeySym in table
+ KeyIDMap::const_iterator i = m_keyIDMap.find(id);
+ if (i == m_keyIDMap.end()) {
+ // unknown key
+ LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id));
+ return NULL;
+ }
+ const KeyGroupTable& keyGroupTable = i->second;
+
+ // find the first key that generates this KeyID
+ const KeyItem* keyItem = NULL;
+ SInt32 numGroups = getNumGroups();
+ for (SInt32 groupOffset = 0; groupOffset < numGroups; ++groupOffset) {
+ SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset);
+ const KeyEntryList& entryList = keyGroupTable[effectiveGroup];
+ for (size_t i = 0; i < entryList.size(); ++i) {
+ if (entryList[i].size() != 1) {
+ // ignore multikey entries
+ continue;
+ }
+
+ // match based on shift and make sure all required modifiers,
+ // except shift, are already in the desired mask; we're
+ // after the right button not the right character.
+ // we'll use desiredMask as-is, overriding the key's required
+ // modifiers, when synthesizing this button.
+ const KeyItem& item = entryList[i].back();
+ KeyModifierMask desiredShiftMask = KeyModifierShift & desiredMask;
+ KeyModifierMask requiredIgnoreShiftMask = item.m_required & ~KeyModifierShift;
+ if ((item.m_required & desiredShiftMask) == (item.m_sensitive & desiredShiftMask) &&
+ ((requiredIgnoreShiftMask & desiredMask) == requiredIgnoreShiftMask)) {
+ LOG((CLOG_INFO "found key in group %d", effectiveGroup));
+ keyItem = &item;
+ break;
+ }
+ }
+ if (keyItem != NULL) {
+ break;
+ }
+ }
+ if (keyItem == NULL) {
+ // no mapping for this keysym
+ LOG((CLOG_DEBUG1 "no mapping for key %04x", id));
+ return NULL;
+ }
+
+ // make working copy of modifiers
+ ModifierToKeys newModifiers = activeModifiers;
+ KeyModifierMask newState = currentState;
+ SInt32 newGroup = group;
+
+ // don't try to change CapsLock
+ desiredMask = (desiredMask & ~KeyModifierCapsLock) |
+ (currentState & KeyModifierCapsLock);
+
+ // add the key
+ if (!keysForKeyItem(*keyItem, newGroup, newModifiers,
+ newState, desiredMask,
+ s_overrideModifiers, isAutoRepeat, keys)) {
+ LOG((CLOG_DEBUG1 "can't map key"));
+ keys.clear();
+ return NULL;
+ }
+
+ // add keystrokes to restore modifier keys
+ if (!keysToRestoreModifiers(*keyItem, group, newModifiers, newState,
+ activeModifiers, keys)) {
+ LOG((CLOG_DEBUG1 "failed to restore modifiers"));
+ keys.clear();
+ return NULL;
+ }
+
+ // add keystrokes to restore group
+ if (newGroup != group) {
+ keys.push_back(Keystroke(group, true, true));
+ }
+
+ // save new modifiers
+ activeModifiers = newModifiers;
+ currentState = newState;
+
+ return keyItem;
+}
+
+const KeyMap::KeyItem*
+KeyMap::mapCharacterKey(Keystrokes& keys, KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const
+{
+ // find KeySym in table
+ KeyIDMap::const_iterator i = m_keyIDMap.find(id);
+ if (i == m_keyIDMap.end()) {
+ // unknown key
+ LOG((CLOG_DEBUG1 "key %04x is not on keyboard", id));
+ return NULL;
+ }
+ const KeyGroupTable& keyGroupTable = i->second;
+
+ // find best key in any group, starting with the active group
+ SInt32 keyIndex = -1;
+ SInt32 numGroups = getNumGroups();
+ SInt32 groupOffset;
+ LOG((CLOG_DEBUG1 "find best: %04x %04x", currentState, desiredMask));
+ for (groupOffset = 0; groupOffset < numGroups; ++groupOffset) {
+ SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset);
+ keyIndex = findBestKey(keyGroupTable[effectiveGroup],
+ currentState, desiredMask);
+ if (keyIndex != -1) {
+ LOG((CLOG_DEBUG1 "found key in group %d", effectiveGroup));
+ break;
+ }
+ }
+ if (keyIndex == -1) {
+ // no mapping for this keysym
+ LOG((CLOG_DEBUG1 "no mapping for key %04x", id));
+ return NULL;
+ }
+
+ // get keys to press for key
+ SInt32 effectiveGroup = getEffectiveGroup(group, groupOffset);
+ const KeyItemList& itemList = keyGroupTable[effectiveGroup][keyIndex];
+ if (itemList.empty()) {
+ return NULL;
+ }
+ const KeyItem& keyItem = itemList.back();
+
+ // make working copy of modifiers
+ ModifierToKeys newModifiers = activeModifiers;
+ KeyModifierMask newState = currentState;
+ SInt32 newGroup = group;
+
+ // add each key
+ for (size_t j = 0; j < itemList.size(); ++j) {
+ if (!keysForKeyItem(itemList[j], newGroup, newModifiers,
+ newState, desiredMask,
+ 0, isAutoRepeat, keys)) {
+ LOG((CLOG_DEBUG1 "can't map key"));
+ keys.clear();
+ return NULL;
+ }
+ }
+
+ // add keystrokes to restore modifier keys
+ if (!keysToRestoreModifiers(keyItem, group, newModifiers, newState,
+ activeModifiers, keys)) {
+ LOG((CLOG_DEBUG1 "failed to restore modifiers"));
+ keys.clear();
+ return NULL;
+ }
+
+ // add keystrokes to restore group
+ if (newGroup != group) {
+ keys.push_back(Keystroke(group, true, true));
+ }
+
+ // save new modifiers
+ activeModifiers = newModifiers;
+ currentState = newState;
+
+ return &keyItem;
+}
+
+const KeyMap::KeyItem*
+KeyMap::mapModifierKey(Keystrokes& keys, KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const
+{
+ return mapCharacterKey(keys, id, group, activeModifiers,
+ currentState, desiredMask, isAutoRepeat);
+}
+
+SInt32
+KeyMap::findBestKey(const KeyEntryList& entryList,
+ KeyModifierMask /*currentState*/,
+ KeyModifierMask desiredState) const
+{
+ // check for an item that can accommodate the desiredState exactly
+ for (SInt32 i = 0; i < (SInt32)entryList.size(); ++i) {
+ const KeyItem& item = entryList[i].back();
+ if ((item.m_required & desiredState) == item.m_required &&
+ (item.m_required & desiredState) == (item.m_sensitive & desiredState)) {
+ LOG((CLOG_DEBUG1 "best key index %d of %d (exact)", i + 1, entryList.size()));
+ return i;
+ }
+ }
+
+ // choose the item that requires the fewest modifier changes
+ SInt32 bestCount = 32;
+ SInt32 bestIndex = -1;
+ for (SInt32 i = 0; i < (SInt32)entryList.size(); ++i) {
+ const KeyItem& item = entryList[i].back();
+ KeyModifierMask change =
+ ((item.m_required ^ desiredState) & item.m_sensitive);
+ SInt32 n = getNumModifiers(change);
+ if (n < bestCount) {
+ bestCount = n;
+ bestIndex = i;
+ }
+ }
+ if (bestIndex != -1) {
+ LOG((CLOG_DEBUG1 "best key index %d of %d (%d modifiers)",
+ bestIndex + 1, entryList.size(), bestCount));
+ }
+
+ return bestIndex;
+}
+
+
+const KeyMap::KeyItem*
+KeyMap::keyForModifier(KeyButton button, SInt32 group,
+ SInt32 modifierBit) const
+{
+ assert(modifierBit >= 0 && modifierBit < kKeyModifierNumBits);
+ assert(group >= 0 && group < getNumGroups());
+
+ // find a key that generates the given modifier in the given group
+ // but doesn't use the given button, presumably because we're trying
+ // to generate a KeyID that's only bound the the given button.
+ // this is important when a shift button is modified by shift; we
+ // must use the other shift button to do the shifting.
+ const ModifierKeyItemList& items =
+ m_modifierKeys[group * kKeyModifierNumBits + modifierBit];
+ for (ModifierKeyItemList::const_iterator i = items.begin();
+ i != items.end(); ++i) {
+ if ((*i)->m_button != button) {
+ return (*i);
+ }
+ }
+ return NULL;
+}
+
+bool
+KeyMap::keysForKeyItem(const KeyItem& keyItem, SInt32& group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState, KeyModifierMask desiredState,
+ KeyModifierMask overrideModifiers,
+ bool isAutoRepeat,
+ Keystrokes& keystrokes) const
+{
+ static const KeyModifierMask s_notRequiredMask =
+ KeyModifierAltGr | KeyModifierNumLock | KeyModifierScrollLock;
+
+ // add keystrokes to adjust the group
+ if (group != keyItem.m_group) {
+ group = keyItem.m_group;
+ keystrokes.push_back(Keystroke(group, true, false));
+ }
+
+ EKeystroke type;
+ if (keyItem.m_dead) {
+ // adjust modifiers for dead key
+ if (!keysForModifierState(keyItem.m_button, group,
+ activeModifiers, currentState,
+ keyItem.m_required, keyItem.m_sensitive,
+ 0, keystrokes)) {
+ LOG((CLOG_DEBUG1 "unable to match modifier state for dead key %d", keyItem.m_button));
+ return false;
+ }
+
+ // press and release the dead key
+ type = kKeystrokeClick;
+ }
+ else {
+ // if this a command key then we don't have to match some of the
+ // key's required modifiers.
+ KeyModifierMask sensitive = keyItem.m_sensitive & ~overrideModifiers;
+
+ // XXX -- must handle pressing a modifier. in particular, if we want
+ // to synthesize a KeyID on level 1 of a KeyButton that has Shift_L
+ // mapped to level 0 then we must release that button if it's down
+ // (in order to satisfy a shift modifier) then press a different
+ // button (any other button) mapped to the shift modifier and then
+ // the Shift_L button.
+ // match key's required state
+ LOG((CLOG_DEBUG1 "state: %04x,%04x,%04x", currentState, keyItem.m_required, sensitive));
+ if (!keysForModifierState(keyItem.m_button, group,
+ activeModifiers, currentState,
+ keyItem.m_required, sensitive,
+ 0, keystrokes)) {
+ LOG((CLOG_DEBUG1 "unable to match modifier state (%04x,%04x) for key %d", keyItem.m_required, keyItem.m_sensitive, keyItem.m_button));
+ return false;
+ }
+
+ // match desiredState as closely as possible. we must not
+ // change any modifiers in keyItem.m_sensitive. and if the key
+ // is a modifier, we don't want to change that modifier.
+ LOG((CLOG_DEBUG1 "desired state: %04x %04x,%04x,%04x", desiredState, currentState, keyItem.m_required, keyItem.m_sensitive));
+ if (!keysForModifierState(keyItem.m_button, group,
+ activeModifiers, currentState,
+ desiredState,
+ ~(sensitive | keyItem.m_generates),
+ s_notRequiredMask, keystrokes)) {
+ LOG((CLOG_DEBUG1 "unable to match desired modifier state (%04x,%04x) for key %d", desiredState, ~keyItem.m_sensitive & 0xffffu, keyItem.m_button));
+ return false;
+ }
+
+ // repeat or press of key
+ type = isAutoRepeat ? kKeystrokeRepeat : kKeystrokePress;
+ }
+ addKeystrokes(type, keyItem, activeModifiers, currentState, keystrokes);
+
+ return true;
+}
+
+bool
+KeyMap::keysToRestoreModifiers(const KeyItem& keyItem, SInt32,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ const ModifierToKeys& desiredModifiers,
+ Keystrokes& keystrokes) const
+{
+ // XXX -- we're not considering modified modifiers here
+
+ ModifierToKeys oldModifiers = activeModifiers;
+
+ // get the pressed modifier buttons before and after
+ ButtonToKeyMap oldKeys, newKeys;
+ collectButtons(oldModifiers, oldKeys);
+ collectButtons(desiredModifiers, newKeys);
+
+ // release unwanted keys
+ for (ModifierToKeys::const_iterator i = oldModifiers.begin();
+ i != oldModifiers.end(); ++i) {
+ KeyButton button = i->second.m_button;
+ if (button != keyItem.m_button && newKeys.count(button) == 0) {
+ EKeystroke type = kKeystrokeRelease;
+ if (i->second.m_lock) {
+ type = kKeystrokeUnmodify;
+ }
+ addKeystrokes(type, i->second,
+ activeModifiers, currentState, keystrokes);
+ }
+ }
+
+ // press wanted keys
+ for (ModifierToKeys::const_iterator i = desiredModifiers.begin();
+ i != desiredModifiers.end(); ++i) {
+ KeyButton button = i->second.m_button;
+ if (button != keyItem.m_button && oldKeys.count(button) == 0) {
+ EKeystroke type = kKeystrokePress;
+ if (i->second.m_lock) {
+ type = kKeystrokeModify;
+ }
+ addKeystrokes(type, i->second,
+ activeModifiers, currentState, keystrokes);
+ }
+ }
+
+ return true;
+}
+
+bool
+KeyMap::keysForModifierState(KeyButton button, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask requiredState, KeyModifierMask sensitiveMask,
+ KeyModifierMask notRequiredMask,
+ Keystrokes& keystrokes) const
+{
+ // compute which modifiers need changing
+ KeyModifierMask flipMask = ((currentState ^ requiredState) & sensitiveMask);
+ // if a modifier is not required then don't even try to match it. if
+ // we don't mask out notRequiredMask then we'll try to match those
+ // modifiers but succeed if we can't. however, this is known not
+ // to work if the key itself is a modifier (the numlock toggle can
+ // interfere) so we don't try to match at all.
+ flipMask &= ~notRequiredMask;
+ LOG((CLOG_DEBUG1 "flip: %04x (%04x vs %04x in %04x - %04x)", flipMask, currentState, requiredState, sensitiveMask & 0xffffu, notRequiredMask & 0xffffu));
+ if (flipMask == 0) {
+ return true;
+ }
+
+ // fix modifiers. this is complicated by the fact that a modifier may
+ // be sensitive to other modifiers! (who thought that up?)
+ //
+ // we'll assume that modifiers with higher bits are affected by modifiers
+ // with lower bits. there's not much basis for that assumption except
+ // that we're pretty sure shift isn't changed by other modifiers.
+ for (SInt32 bit = kKeyModifierNumBits; bit-- > 0; ) {
+ KeyModifierMask mask = (1u << bit);
+ if ((flipMask & mask) == 0) {
+ // modifier is already correct
+ continue;
+ }
+
+ // do we want the modifier active or inactive?
+ bool active = ((requiredState & mask) != 0);
+
+ // get the KeyItem for the modifier in the group
+ const KeyItem* keyItem = keyForModifier(button, group, bit);
+ if (keyItem == NULL) {
+ if ((mask & notRequiredMask) == 0) {
+ LOG((CLOG_DEBUG1 "no key for modifier %04x", mask));
+ return false;
+ }
+ else {
+ continue;
+ }
+ }
+
+ // if this modifier is sensitive to modifiers then adjust those
+ // modifiers. also check if our assumption was correct. note
+ // that we only need to adjust the modifiers on key down.
+ KeyModifierMask sensitive = keyItem->m_sensitive;
+ if ((sensitive & mask) != 0) {
+ // modifier is sensitive to itself. that makes no sense
+ // so ignore it.
+ LOG((CLOG_DEBUG1 "modifier %04x modified by itself", mask));
+ sensitive &= ~mask;
+ }
+ if (sensitive != 0) {
+ if (sensitive > mask) {
+ // our assumption is incorrect
+ LOG((CLOG_DEBUG1 "modifier %04x modified by %04x", mask, sensitive));
+ return false;
+ }
+ if (active && !keysForModifierState(button, group,
+ activeModifiers, currentState,
+ keyItem->m_required, sensitive,
+ notRequiredMask, keystrokes)) {
+ return false;
+ }
+ else if (!active) {
+ // release the modifier
+ // XXX -- this doesn't work! if Alt and Meta are mapped
+ // to one key and we want to release Meta we can't do
+ // that without also releasing Alt.
+ // need to think about support for modified modifiers.
+ }
+ }
+
+ // current state should match required state
+ if ((currentState & sensitive) != (keyItem->m_required & sensitive)) {
+ LOG((CLOG_DEBUG1 "unable to match modifier state for modifier %04x (%04x vs %04x in %04x)", mask, currentState, keyItem->m_required, sensitive));
+ return false;
+ }
+
+ // add keystrokes
+ EKeystroke type = active ? kKeystrokeModify : kKeystrokeUnmodify;
+ addKeystrokes(type, *keyItem, activeModifiers, currentState,
+ keystrokes);
+ }
+
+ return true;
+}
+
+void
+KeyMap::addKeystrokes(EKeystroke type, const KeyItem& keyItem,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ Keystrokes& keystrokes) const
+{
+ KeyButton button = keyItem.m_button;
+ UInt32 data = keyItem.m_client;
+ switch (type) {
+ case kKeystrokePress:
+ keystrokes.push_back(Keystroke(button, true, false, data));
+ if (keyItem.m_generates != 0) {
+ if (!keyItem.m_lock || (currentState & keyItem.m_generates) == 0) {
+ // add modifier key and activate modifier
+ activeModifiers.insert(std::make_pair(
+ keyItem.m_generates, keyItem));
+ currentState |= keyItem.m_generates;
+ }
+ else {
+ // deactivate locking modifier
+ activeModifiers.erase(keyItem.m_generates);
+ currentState &= ~keyItem.m_generates;
+ }
+ }
+ break;
+
+ case kKeystrokeRelease:
+ keystrokes.push_back(Keystroke(button, false, false, data));
+ if (keyItem.m_generates != 0 && !keyItem.m_lock) {
+ // remove key from active modifiers
+ std::pair<ModifierToKeys::iterator,
+ ModifierToKeys::iterator> range =
+ activeModifiers.equal_range(keyItem.m_generates);
+ for (ModifierToKeys::iterator i = range.first;
+ i != range.second; ++i) {
+ if (i->second.m_button == button) {
+ activeModifiers.erase(i);
+ break;
+ }
+ }
+
+ // if no more keys for this modifier then deactivate modifier
+ if (activeModifiers.count(keyItem.m_generates) == 0) {
+ currentState &= ~keyItem.m_generates;
+ }
+ }
+ break;
+
+ case kKeystrokeRepeat:
+ keystrokes.push_back(Keystroke(button, false, true, data));
+ keystrokes.push_back(Keystroke(button, true, true, data));
+ // no modifier changes on key repeat
+ break;
+
+ case kKeystrokeClick:
+ keystrokes.push_back(Keystroke(button, true, false, data));
+ keystrokes.push_back(Keystroke(button, false, false, data));
+ // no modifier changes on key click
+ break;
+
+ case kKeystrokeModify:
+ case kKeystrokeUnmodify:
+ if (keyItem.m_lock) {
+ // we assume there's just one button for this modifier
+ if (m_halfDuplex.count(button) > 0) {
+ if (type == kKeystrokeModify) {
+ // turn half-duplex toggle on (press)
+ keystrokes.push_back(Keystroke(button, true, false, data));
+ }
+ else {
+ // turn half-duplex toggle off (release)
+ keystrokes.push_back(Keystroke(button, false, false, data));
+ }
+ }
+ else {
+ // toggle (click)
+ keystrokes.push_back(Keystroke(button, true, false, data));
+ keystrokes.push_back(Keystroke(button, false, false, data));
+ }
+ }
+ else if (type == kKeystrokeModify) {
+ // press modifier
+ keystrokes.push_back(Keystroke(button, true, false, data));
+ }
+ else {
+ // release all the keys that generate the modifier that are
+ // currently down
+ std::pair<ModifierToKeys::const_iterator,
+ ModifierToKeys::const_iterator> range =
+ activeModifiers.equal_range(keyItem.m_generates);
+ for (ModifierToKeys::const_iterator i = range.first;
+ i != range.second; ++i) {
+ keystrokes.push_back(Keystroke(i->second.m_button,
+ false, false, i->second.m_client));
+ }
+ }
+
+ if (type == kKeystrokeModify) {
+ activeModifiers.insert(std::make_pair(
+ keyItem.m_generates, keyItem));
+ currentState |= keyItem.m_generates;
+ }
+ else {
+ activeModifiers.erase(keyItem.m_generates);
+ currentState &= ~keyItem.m_generates;
+ }
+ break;
+ }
+}
+
+SInt32
+KeyMap::getNumModifiers(KeyModifierMask state)
+{
+ SInt32 n = 0;
+ for (; state != 0; state >>= 1) {
+ if ((state & 1) != 0) {
+ ++n;
+ }
+ }
+ return n;
+}
+
+bool
+KeyMap::isDeadKey(KeyID key)
+{
+ return (key == kKeyCompose || (key >= 0x0300 && key <= 0x036f));
+}
+
+KeyID
+KeyMap::getDeadKey(KeyID key)
+{
+ if (isDeadKey(key)) {
+ // already dead
+ return key;
+ }
+
+ switch (key) {
+ case '`':
+ return kKeyDeadGrave;
+
+ case 0xb4u:
+ return kKeyDeadAcute;
+
+ case '^':
+ case 0x2c6:
+ return kKeyDeadCircumflex;
+
+ case '~':
+ case 0x2dcu:
+ return kKeyDeadTilde;
+
+ case 0xafu:
+ return kKeyDeadMacron;
+
+ case 0x2d8u:
+ return kKeyDeadBreve;
+
+ case 0x2d9u:
+ return kKeyDeadAbovedot;
+
+ case 0xa8u:
+ return kKeyDeadDiaeresis;
+
+ case 0xb0u:
+ case 0x2dau:
+ return kKeyDeadAbovering;
+
+ case '\"':
+ case 0x2ddu:
+ return kKeyDeadDoubleacute;
+
+ case 0x2c7u:
+ return kKeyDeadCaron;
+
+ case 0xb8u:
+ return kKeyDeadCedilla;
+
+ case 0x2dbu:
+ return kKeyDeadOgonek;
+
+ default:
+ // unknown
+ return kKeyNone;
+ }
+}
+
+String
+KeyMap::formatKey(KeyID key, KeyModifierMask mask)
+{
+ // initialize tables
+ initKeyNameMaps();
+
+ String x;
+ for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) {
+ KeyModifierMask mod = (1u << i);
+ if ((mask & mod) != 0 && s_modifierToNameMap->count(mod) > 0) {
+ x += s_modifierToNameMap->find(mod)->second;
+ x += "+";
+ }
+ }
+ if (key != kKeyNone) {
+ if (s_keyToNameMap->count(key) > 0) {
+ x += s_keyToNameMap->find(key)->second;
+ }
+ // XXX -- we're assuming ASCII here
+ else if (key >= 33 && key < 127) {
+ x += (char)key;
+ }
+ else {
+ x += barrier::string::sprintf("\\u%04x", key);
+ }
+ }
+ else if (!x.empty()) {
+ // remove trailing '+'
+ x.erase(x.size() - 1);
+ }
+ return x;
+}
+
+bool
+KeyMap::parseKey(const String& x, KeyID& key)
+{
+ // initialize tables
+ initKeyNameMaps();
+
+ // parse the key
+ key = kKeyNone;
+ if (s_nameToKeyMap->count(x) > 0) {
+ key = s_nameToKeyMap->find(x)->second;
+ }
+ // XXX -- we're assuming ASCII encoding here
+ else if (x.size() == 1) {
+ if (!isgraph(x[0])) {
+ // unknown key
+ return false;
+ }
+ key = (KeyID)x[0];
+ }
+ else if (x.size() == 6 && x[0] == '\\' && x[1] == 'u') {
+ // escaped unicode (\uXXXX where XXXX is a hex number)
+ char* end;
+ key = (KeyID)strtol(x.c_str() + 2, &end, 16);
+ if (*end != '\0') {
+ return false;
+ }
+ }
+ else if (!x.empty()) {
+ // unknown key
+ return false;
+ }
+
+ return true;
+}
+
+bool
+KeyMap::parseModifiers(String& x, KeyModifierMask& mask)
+{
+ // initialize tables
+ initKeyNameMaps();
+
+ mask = 0;
+ String::size_type tb = x.find_first_not_of(" \t", 0);
+ while (tb != String::npos) {
+ // get next component
+ String::size_type te = x.find_first_of(" \t+)", tb);
+ if (te == String::npos) {
+ te = x.size();
+ }
+ String c = x.substr(tb, te - tb);
+ if (c.empty()) {
+ // missing component
+ return false;
+ }
+
+ if (s_nameToModifierMap->count(c) > 0) {
+ KeyModifierMask mod = s_nameToModifierMap->find(c)->second;
+ if ((mask & mod) != 0) {
+ // modifier appears twice
+ return false;
+ }
+ mask |= mod;
+ }
+ else {
+ // unknown string
+ x.erase(0, tb);
+ String::size_type tb = x.find_first_not_of(" \t");
+ String::size_type te = x.find_last_not_of(" \t");
+ if (tb == String::npos) {
+ x = "";
+ }
+ else {
+ x = x.substr(tb, te - tb + 1);
+ }
+ return true;
+ }
+
+ // check for '+' or end of string
+ tb = x.find_first_not_of(" \t", te);
+ if (tb != String::npos) {
+ if (x[tb] != '+') {
+ // expected '+'
+ return false;
+ }
+ tb = x.find_first_not_of(" \t", tb + 1);
+ }
+ }
+
+ // parsed the whole thing
+ x = "";
+ return true;
+}
+
+void
+KeyMap::initKeyNameMaps()
+{
+ // initialize tables
+ if (s_nameToKeyMap == NULL) {
+ s_nameToKeyMap = new NameToKeyMap;
+ s_keyToNameMap = new KeyToNameMap;
+ for (const KeyNameMapEntry* i = kKeyNameMap; i->m_name != NULL; ++i) {
+ (*s_nameToKeyMap)[i->m_name] = i->m_id;
+ (*s_keyToNameMap)[i->m_id] = i->m_name;
+ }
+ }
+ if (s_nameToModifierMap == NULL) {
+ s_nameToModifierMap = new NameToModifierMap;
+ s_modifierToNameMap = new ModifierToNameMap;
+ for (const KeyModifierNameMapEntry* i = kModifierNameMap;
+ i->m_name != NULL; ++i) {
+ (*s_nameToModifierMap)[i->m_name] = i->m_mask;
+ (*s_modifierToNameMap)[i->m_mask] = i->m_name;
+ }
+ }
+}
+
+
+//
+// KeyMap::KeyItem
+//
+
+bool
+KeyMap::KeyItem::operator==(const KeyItem& x) const
+{
+ return (m_id == x.m_id &&
+ m_group == x.m_group &&
+ m_button == x.m_button &&
+ m_required == x.m_required &&
+ m_sensitive == x.m_sensitive &&
+ m_generates == x.m_generates &&
+ m_dead == x.m_dead &&
+ m_lock == x.m_lock &&
+ m_client == x.m_client);
+}
+
+
+//
+// KeyMap::Keystroke
+//
+
+KeyMap::Keystroke::Keystroke(KeyButton button,
+ bool press, bool repeat, UInt32 data) :
+ m_type(kButton)
+{
+ m_data.m_button.m_button = button;
+ m_data.m_button.m_press = press;
+ m_data.m_button.m_repeat = repeat;
+ m_data.m_button.m_client = data;
+}
+
+KeyMap::Keystroke::Keystroke(SInt32 group, bool absolute, bool restore) :
+ m_type(kGroup)
+{
+ m_data.m_group.m_group = group;
+ m_data.m_group.m_absolute = absolute;
+ m_data.m_group.m_restore = restore;
+}
+
+}
diff --git a/src/lib/barrier/KeyMap.h b/src/lib/barrier/KeyMap.h
new file mode 100644
index 0000000..b6eb865
--- /dev/null
+++ b/src/lib/barrier/KeyMap.h
@@ -0,0 +1,512 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2005 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 "barrier/key_types.h"
+#include "base/String.h"
+#include "common/stdmap.h"
+#include "common/stdset.h"
+#include "common/stdvector.h"
+
+#include <gtest/gtest_prod.h>
+
+namespace barrier {
+
+//! Key map
+/*!
+This class provides a keyboard mapping.
+*/
+class KeyMap {
+public:
+ KeyMap();
+ virtual ~KeyMap();
+
+ //! KeyID synthesis info
+ /*!
+ This structure contains the information necessary to synthesize a
+ keystroke that generates a KeyID (stored elsewhere). \c m_sensitive
+ lists the modifiers that the key is affected by and must therefore
+ be in the correct state, which is listed in \c m_required. If the
+ key is mapped to a modifier, that modifier is in \c m_generates and
+ is not in \c m_sensitive.
+ */
+ struct KeyItem {
+ public:
+ KeyID m_id; //!< KeyID
+ SInt32 m_group; //!< Group for key
+ KeyButton m_button; //!< Button to generate KeyID
+ KeyModifierMask m_required; //!< Modifiers required for KeyID
+ KeyModifierMask m_sensitive; //!< Modifiers key is sensitive to
+ KeyModifierMask m_generates; //!< Modifiers key is mapped to
+ bool m_dead; //!< \c true if this is a dead KeyID
+ bool m_lock; //!< \c true if this locks a modifier
+ UInt32 m_client; //!< Client data
+
+ public:
+ bool operator==(const KeyItem&) const;
+ };
+
+ //! The KeyButtons needed to synthesize a KeyID
+ /*!
+ An ordered list of \c KeyItems produces a particular KeyID. If
+ the KeyID can be synthesized directly then there is one entry in
+ the list. If dead keys are required then they're listed first.
+ A list is the minimal set of keystrokes necessary to synthesize
+ the KeyID, so it doesn't include no-ops. A list does not include
+ any modifier keys unless the KeyID is a modifier, in which case
+ it has exactly one KeyItem for the modifier itself.
+ */
+ typedef std::vector<KeyItem> KeyItemList;
+
+ //! A keystroke
+ class Keystroke {
+ public:
+ enum EType {
+ kButton, //!< Synthesize button
+ kGroup //!< Set new group
+ };
+
+ Keystroke(KeyButton, bool press, bool repeat, UInt32 clientData);
+ Keystroke(SInt32 group, bool absolute, bool restore);
+
+ public:
+ struct Button {
+ public:
+ KeyButton m_button; //!< Button to synthesize
+ bool m_press; //!< \c true iff press
+ bool m_repeat; //!< \c true iff for an autorepeat
+ UInt32 m_client; //!< Client data
+ };
+ struct Group {
+ public:
+ SInt32 m_group; //!< Group/offset to change to/by
+ bool m_absolute; //!< \c true iff change to, else by
+ bool m_restore; //!< \c true iff for restoring state
+ };
+ union Data {
+ public:
+ Button m_button;
+ Group m_group;
+ };
+
+ EType m_type;
+ Data m_data;
+ };
+
+ //! A sequence of keystrokes
+ typedef std::vector<Keystroke> Keystrokes;
+
+ //! A mapping of a modifier to keys for that modifier
+ typedef std::multimap<KeyModifierMask, KeyItem> ModifierToKeys;
+
+ //! A set of buttons
+ typedef std::map<KeyButton, const KeyItem*> ButtonToKeyMap;
+
+ //! Callback type for \c foreachKey
+ typedef void (*ForeachKeyCallback)(KeyID, SInt32 group,
+ KeyItem&, void* userData);
+
+ //! @name manipulators
+ //@{
+
+ //! Swap with another \c KeyMap
+ virtual void swap(KeyMap&);
+
+ //! Add a key entry
+ /*!
+ Adds \p item to the entries for the item's id and group. The
+ \c m_dead member is set automatically.
+ */
+ void addKeyEntry(const KeyItem& item);
+
+ //! Add an alias key entry
+ /*!
+ If \p targetID with the modifiers given by \p targetRequired and
+ \p targetSensitive is not available in group \p group then find an
+ entry for \p sourceID with modifiers given by \p sourceRequired and
+ \p sourceSensitive in any group with exactly one item and, if found,
+ add a new item just like it except using id \p targetID. This
+ effectively makes the \p sourceID an alias for \p targetID (i.e. we
+ can generate \p targetID using \p sourceID).
+ */
+ void addKeyAliasEntry(KeyID targetID, SInt32 group,
+ KeyModifierMask targetRequired,
+ KeyModifierMask targetSensitive,
+ KeyID sourceID,
+ KeyModifierMask sourceRequired,
+ KeyModifierMask sourceSensitive);
+
+ //! Add a key sequence entry
+ /*!
+ Adds the sequence of keys \p keys (\p numKeys elements long) to
+ synthesize key \p id in group \p group. This looks up in the
+ map each key in \p keys. If all are found then each key is
+ converted to the button for that key and the buttons are added
+ as the entry for \p id. If \p id is already in the map or at
+ least one key in \p keys is not in the map then nothing is added
+ and this returns \c false, otherwise it returns \c true.
+ */
+ bool addKeyCombinationEntry(KeyID id, SInt32 group,
+ const KeyID* keys, UInt32 numKeys);
+
+ //! Enable composition across groups
+ /*!
+ If called then the keyboard map will allow switching between groups
+ during key composition. Not all systems allow that.
+ */
+ void allowGroupSwitchDuringCompose();
+
+ //! Add a half-duplex button
+ /*!
+ Records that button \p button is a half-duplex key. This is called
+ when translating the system's keyboard map. It's independent of the
+ half-duplex modifier calls.
+ */
+ void addHalfDuplexButton(KeyButton button);
+
+ //! Remove all half-duplex modifiers
+ /*!
+ Removes all half-duplex modifiers. This is called to set user
+ configurable half-duplex settings.
+ */
+ void clearHalfDuplexModifiers();
+
+ //! Add a half-duplex modifier
+ /*!
+ Records that modifier key \p key is half-duplex. This is called to
+ set user configurable half-duplex settings.
+ */
+ virtual void addHalfDuplexModifier(KeyID key);
+
+ //! Finish adding entries
+ /*!
+ Called after adding entries, this does some internal housekeeping.
+ */
+ virtual void finish();
+
+ //! Iterate over all added keys items
+ /*!
+ Calls \p cb for every key item.
+ */
+ virtual void foreachKey(ForeachKeyCallback cb, void* userData);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Map key press/repeat to keystrokes.
+ /*!
+ Converts press/repeat of key \p id in group \p group with current
+ modifiers as given in \p currentState and the desired modifiers in
+ \p desiredMask into the keystrokes necessary to synthesize that key
+ event in \p keys. It returns the \c KeyItem of the key being
+ pressed/repeated, or NULL if the key cannot be mapped.
+ */
+ virtual const KeyItem* mapKey(Keystrokes& keys, KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const;
+
+ //! Get number of groups
+ /*!
+ Returns the number of keyboard groups (independent layouts) in the map.
+ */
+ SInt32 getNumGroups() const;
+
+ //! Compute a group number
+ /*!
+ Returns the number of the group \p offset groups after group \p group.
+ */
+ SInt32 getEffectiveGroup(SInt32 group, SInt32 offset) const;
+
+ //! Find key entry compatible with modifiers
+ /*!
+ Returns the \c KeyItemList for the first entry for \p id in group
+ \p group that is compatible with the given modifiers, or NULL
+ if there isn't one. A button list is compatible with a modifiers
+ if it is either insensitive to all modifiers in \p sensitive or
+ it requires the modifiers to be in the state indicated by \p required
+ for every modifier indicated by \p sensitive.
+ */
+ const KeyItemList* findCompatibleKey(KeyID id, SInt32 group,
+ KeyModifierMask required,
+ KeyModifierMask sensitive) const;
+
+ //! Test if modifier is half-duplex
+ /*!
+ Returns \c true iff modifier key \p key or button \p button is
+ half-duplex.
+ */
+ virtual bool isHalfDuplex(KeyID key, KeyButton button) const;
+
+ //! Test if modifiers indicate a command
+ /*!
+ Returns \c true iff the modifiers in \p mask contain any command
+ modifiers. A command modifier is used for keyboard shortcuts and
+ hotkeys, Rather than trying to synthesize a character, a command
+ is trying to synthesize a particular set of buttons. So it's not
+ important to match the shift or AltGr state to achieve a character
+ but it is important to match the modifier state exactly.
+ */
+ bool isCommand(KeyModifierMask mask) const;
+
+ // Get the modifiers that indicate a command
+ /*!
+ Returns the modifiers that when combined with other keys indicate
+ a command (e.g. shortcut or hotkey).
+ */
+ KeyModifierMask getCommandModifiers() const;
+
+ //! Get buttons from modifier map
+ /*!
+ Put all the keys in \p modifiers into \p keys.
+ */
+ static void collectButtons(const ModifierToKeys& modifiers,
+ ButtonToKeyMap& keys);
+
+ //! Set modifier key state
+ /*!
+ Sets the modifier key state (\c m_generates and \c m_lock) in \p item
+ based on the \c m_id in \p item.
+ */
+ static void initModifierKey(KeyItem& item);
+
+ //! Test for a dead key
+ /*!
+ Returns \c true if \p key is a dead key.
+ */
+ static bool isDeadKey(KeyID key);
+
+ //! Get corresponding dead key
+ /*!
+ Returns the dead key corresponding to \p key if one exists, otherwise
+ return \c kKeyNone. This returns \p key if it's already a dead key.
+ */
+ static KeyID getDeadKey(KeyID key);
+
+ //! Get string for a key and modifier mask
+ /*!
+ Converts a key and modifier mask into a string representing the
+ combination.
+ */
+ static String formatKey(KeyID key, KeyModifierMask);
+
+ //! Parse a string into a key
+ /*!
+ Converts a string into a key. Returns \c true on success and \c false
+ if the string cannot be parsed.
+ */
+ static bool parseKey(const String&, KeyID&);
+
+ //! Parse a string into a modifier mask
+ /*!
+ Converts a string into a modifier mask. Returns \c true on success
+ and \c false if the string cannot be parsed. The modifiers plus any
+ remaining leading and trailing whitespace is stripped from the input
+ string.
+ */
+ static bool parseModifiers(String&, KeyModifierMask&);
+
+ //@}
+
+private:
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_requiredDown_matchExactFirstItem);
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_requiredAndExtraSensitiveDown_matchExactFirstItem);
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_requiredAndExtraSensitiveDown_matchExactSecondItem);
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_extraSensitiveDown_matchExactSecondItem);
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_noRequiredDown_matchOneRequiredChangeItem);
+ FRIEND_TEST(KeyMapTests,
+ findBestKey_onlyOneRequiredDown_matchTwoRequiredChangesItem);
+ FRIEND_TEST(KeyMapTests, findBestKey_noRequiredDown_cannotMatch);
+
+private:
+ //! Ways to synthesize a key
+ enum EKeystroke {
+ kKeystrokePress, //!< Synthesize a press
+ kKeystrokeRelease, //!< Synthesize a release
+ kKeystrokeRepeat, //!< Synthesize an autorepeat
+ kKeystrokeClick, //!< Synthesize a press and release
+ kKeystrokeModify, //!< Synthesize pressing a modifier
+ kKeystrokeUnmodify //!< Synthesize releasing a modifier
+ };
+
+ // A list of ways to synthesize a KeyID
+ typedef std::vector<KeyItemList> KeyEntryList;
+
+ // computes the number of groups
+ SInt32 findNumGroups() const;
+
+ // computes the map of modifiers to the keys that generate the modifiers
+ void setModifierKeys();
+
+ // maps a command key. a command key is a keyboard shortcut and we're
+ // trying to synthesize a button press with an exact sets of modifiers,
+ // not trying to synthesize a character. so we just need to find the
+ // right button and synthesize the requested modifiers without regard
+ // to what character they would synthesize. we disallow multikey
+ // entries since they don't make sense as hotkeys.
+ const KeyItem* mapCommandKey(Keystrokes& keys,
+ KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const;
+
+ // maps a character key. a character key is trying to synthesize a
+ // particular KeyID and isn't entirely concerned with the modifiers
+ // used to do it.
+ const KeyItem* mapCharacterKey(Keystrokes& keys,
+ KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const;
+
+ // maps a modifier key
+ const KeyItem* mapModifierKey(Keystrokes& keys,
+ KeyID id, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat) const;
+
+ // returns the index into \p entryList of the KeyItemList requiring
+ // the fewest modifier changes between \p currentState and
+ // \p desiredState.
+ SInt32 findBestKey(const KeyEntryList& entryList,
+ KeyModifierMask currentState,
+ KeyModifierMask desiredState) const;
+
+ // gets the \c KeyItem used to synthesize the modifier who's bit is
+ // given by \p modifierBit in group \p group and does not synthesize
+ // the key \p button.
+ const KeyItem* keyForModifier(KeyButton button, SInt32 group,
+ SInt32 modifierBit) const;
+
+ // fills \p keystrokes with the keys to synthesize the key in
+ // \p keyItem taking the modifiers into account. returns \c true
+ // iff successful and sets \p currentState to the
+ // resulting modifier state.
+ bool keysForKeyItem(const KeyItem& keyItem,
+ SInt32& group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredState,
+ KeyModifierMask overrideModifiers,
+ bool isAutoRepeat,
+ Keystrokes& keystrokes) const;
+
+ // fills \p keystrokes with the keys to synthesize the modifiers
+ // in \p desiredModifiers from the active modifiers listed in
+ // \p activeModifiers not including the key in \p keyItem.
+ // returns \c true iff successful.
+ bool keysToRestoreModifiers(const KeyItem& keyItem,
+ SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ const ModifierToKeys& desiredModifiers,
+ Keystrokes& keystrokes) const;
+
+ // fills \p keystrokes and \p undo with the keys to change the
+ // current modifier state in \p currentState to match the state in
+ // \p requiredState for each modifier indicated in \p sensitiveMask.
+ // returns \c true iff successful and sets \p currentState to the
+ // resulting modifier state.
+ bool keysForModifierState(KeyButton button, SInt32 group,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask requiredState,
+ KeyModifierMask sensitiveMask,
+ KeyModifierMask notRequiredMask,
+ Keystrokes& keystrokes) const;
+
+ // Adds keystrokes to synthesize key \p keyItem in mode \p type to
+ // \p keystrokes and to undo the synthesis to \p undo.
+ void addKeystrokes(EKeystroke type,
+ const KeyItem& keyItem,
+ ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ Keystrokes& keystrokes) const;
+
+ // Returns the number of modifiers indicated in \p state.
+ static SInt32 getNumModifiers(KeyModifierMask state);
+
+ // Initialize key name/id maps
+ static void initKeyNameMaps();
+
+ // not implemented
+ KeyMap(const KeyMap&);
+ KeyMap& operator=(const KeyMap&);
+
+private:
+ // Ways to synthesize a KeyID over multiple keyboard groups
+ typedef std::vector<KeyEntryList> KeyGroupTable;
+
+ // Table of KeyID to ways to synthesize that KeyID
+ typedef std::map<KeyID, KeyGroupTable> KeyIDMap;
+
+ // List of KeyItems that generate a particular modifier
+ typedef std::vector<const KeyItem*> ModifierKeyItemList;
+
+ // Map a modifier to the KeyItems that synthesize that modifier
+ typedef std::vector<ModifierKeyItemList> ModifierToKeyTable;
+
+ // A set of keys
+ typedef std::set<KeyID> KeySet;
+
+ // A set of buttons
+ typedef std::set<KeyButton> KeyButtonSet;
+
+ // Key maps for parsing/formatting
+ typedef std::map<String, KeyID,
+ barrier::string::CaselessCmp> NameToKeyMap;
+ typedef std::map<String, KeyModifierMask,
+ barrier::string::CaselessCmp> NameToModifierMap;
+ typedef std::map<KeyID, String> KeyToNameMap;
+ typedef std::map<KeyModifierMask, String> ModifierToNameMap;
+
+ // KeyID info
+ KeyIDMap m_keyIDMap;
+ SInt32 m_numGroups;
+ ModifierToKeyTable m_modifierKeys;
+
+ // composition info
+ bool m_composeAcrossGroups;
+
+ // half-duplex info
+ KeyButtonSet m_halfDuplex; // half-duplex set by barrier
+ KeySet m_halfDuplexMods; // half-duplex set by user
+
+ // dummy KeyItem for changing modifiers
+ KeyItem m_modifierKeyItem;
+
+ // parsing/formatting tables
+ static NameToKeyMap* s_nameToKeyMap;
+ static NameToModifierMap* s_nameToModifierMap;
+ static KeyToNameMap* s_keyToNameMap;
+ static ModifierToNameMap* s_modifierToNameMap;
+};
+
+}
diff --git a/src/lib/barrier/KeyState.cpp b/src/lib/barrier/KeyState.cpp
new file mode 100644
index 0000000..fc5579d
--- /dev/null
+++ b/src/lib/barrier/KeyState.cpp
@@ -0,0 +1,936 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/KeyState.h"
+#include "base/Log.h"
+
+#include <cstring>
+#include <algorithm>
+#include <iterator>
+#include <list>
+
+static const KeyButton kButtonMask = (KeyButton)(IKeyState::kNumButtons - 1);
+
+static const KeyID s_decomposeTable[] = {
+ // spacing version of dead keys
+ 0x0060, 0x0300, 0x0020, 0, // grave, dead_grave, space
+ 0x00b4, 0x0301, 0x0020, 0, // acute, dead_acute, space
+ 0x005e, 0x0302, 0x0020, 0, // asciicircum, dead_circumflex, space
+ 0x007e, 0x0303, 0x0020, 0, // asciitilde, dead_tilde, space
+ 0x00a8, 0x0308, 0x0020, 0, // diaeresis, dead_diaeresis, space
+ 0x00b0, 0x030a, 0x0020, 0, // degree, dead_abovering, space
+ 0x00b8, 0x0327, 0x0020, 0, // cedilla, dead_cedilla, space
+ 0x02db, 0x0328, 0x0020, 0, // ogonek, dead_ogonek, space
+ 0x02c7, 0x030c, 0x0020, 0, // caron, dead_caron, space
+ 0x02d9, 0x0307, 0x0020, 0, // abovedot, dead_abovedot, space
+ 0x02dd, 0x030b, 0x0020, 0, // doubleacute, dead_doubleacute, space
+ 0x02d8, 0x0306, 0x0020, 0, // breve, dead_breve, space
+ 0x00af, 0x0304, 0x0020, 0, // macron, dead_macron, space
+
+ // Latin-1 (ISO 8859-1)
+ 0x00c0, 0x0300, 0x0041, 0, // Agrave, dead_grave, A
+ 0x00c1, 0x0301, 0x0041, 0, // Aacute, dead_acute, A
+ 0x00c2, 0x0302, 0x0041, 0, // Acircumflex, dead_circumflex, A
+ 0x00c3, 0x0303, 0x0041, 0, // Atilde, dead_tilde, A
+ 0x00c4, 0x0308, 0x0041, 0, // Adiaeresis, dead_diaeresis, A
+ 0x00c5, 0x030a, 0x0041, 0, // Aring, dead_abovering, A
+ 0x00c7, 0x0327, 0x0043, 0, // Ccedilla, dead_cedilla, C
+ 0x00c8, 0x0300, 0x0045, 0, // Egrave, dead_grave, E
+ 0x00c9, 0x0301, 0x0045, 0, // Eacute, dead_acute, E
+ 0x00ca, 0x0302, 0x0045, 0, // Ecircumflex, dead_circumflex, E
+ 0x00cb, 0x0308, 0x0045, 0, // Ediaeresis, dead_diaeresis, E
+ 0x00cc, 0x0300, 0x0049, 0, // Igrave, dead_grave, I
+ 0x00cd, 0x0301, 0x0049, 0, // Iacute, dead_acute, I
+ 0x00ce, 0x0302, 0x0049, 0, // Icircumflex, dead_circumflex, I
+ 0x00cf, 0x0308, 0x0049, 0, // Idiaeresis, dead_diaeresis, I
+ 0x00d1, 0x0303, 0x004e, 0, // Ntilde, dead_tilde, N
+ 0x00d2, 0x0300, 0x004f, 0, // Ograve, dead_grave, O
+ 0x00d3, 0x0301, 0x004f, 0, // Oacute, dead_acute, O
+ 0x00d4, 0x0302, 0x004f, 0, // Ocircumflex, dead_circumflex, O
+ 0x00d5, 0x0303, 0x004f, 0, // Otilde, dead_tilde, O
+ 0x00d6, 0x0308, 0x004f, 0, // Odiaeresis, dead_diaeresis, O
+ 0x00d9, 0x0300, 0x0055, 0, // Ugrave, dead_grave, U
+ 0x00da, 0x0301, 0x0055, 0, // Uacute, dead_acute, U
+ 0x00db, 0x0302, 0x0055, 0, // Ucircumflex, dead_circumflex, U
+ 0x00dc, 0x0308, 0x0055, 0, // Udiaeresis, dead_diaeresis, U
+ 0x00dd, 0x0301, 0x0059, 0, // Yacute, dead_acute, Y
+ 0x00e0, 0x0300, 0x0061, 0, // agrave, dead_grave, a
+ 0x00e1, 0x0301, 0x0061, 0, // aacute, dead_acute, a
+ 0x00e2, 0x0302, 0x0061, 0, // acircumflex, dead_circumflex, a
+ 0x00e3, 0x0303, 0x0061, 0, // atilde, dead_tilde, a
+ 0x00e4, 0x0308, 0x0061, 0, // adiaeresis, dead_diaeresis, a
+ 0x00e5, 0x030a, 0x0061, 0, // aring, dead_abovering, a
+ 0x00e7, 0x0327, 0x0063, 0, // ccedilla, dead_cedilla, c
+ 0x00e8, 0x0300, 0x0065, 0, // egrave, dead_grave, e
+ 0x00e9, 0x0301, 0x0065, 0, // eacute, dead_acute, e
+ 0x00ea, 0x0302, 0x0065, 0, // ecircumflex, dead_circumflex, e
+ 0x00eb, 0x0308, 0x0065, 0, // ediaeresis, dead_diaeresis, e
+ 0x00ec, 0x0300, 0x0069, 0, // igrave, dead_grave, i
+ 0x00ed, 0x0301, 0x0069, 0, // iacute, dead_acute, i
+ 0x00ee, 0x0302, 0x0069, 0, // icircumflex, dead_circumflex, i
+ 0x00ef, 0x0308, 0x0069, 0, // idiaeresis, dead_diaeresis, i
+ 0x00f1, 0x0303, 0x006e, 0, // ntilde, dead_tilde, n
+ 0x00f2, 0x0300, 0x006f, 0, // ograve, dead_grave, o
+ 0x00f3, 0x0301, 0x006f, 0, // oacute, dead_acute, o
+ 0x00f4, 0x0302, 0x006f, 0, // ocircumflex, dead_circumflex, o
+ 0x00f5, 0x0303, 0x006f, 0, // otilde, dead_tilde, o
+ 0x00f6, 0x0308, 0x006f, 0, // odiaeresis, dead_diaeresis, o
+ 0x00f9, 0x0300, 0x0075, 0, // ugrave, dead_grave, u
+ 0x00fa, 0x0301, 0x0075, 0, // uacute, dead_acute, u
+ 0x00fb, 0x0302, 0x0075, 0, // ucircumflex, dead_circumflex, u
+ 0x00fc, 0x0308, 0x0075, 0, // udiaeresis, dead_diaeresis, u
+ 0x00fd, 0x0301, 0x0079, 0, // yacute, dead_acute, y
+ 0x00ff, 0x0308, 0x0079, 0, // ydiaeresis, dead_diaeresis, y
+
+ // Latin-2 (ISO 8859-2)
+ 0x0104, 0x0328, 0x0041, 0, // Aogonek, dead_ogonek, A
+ 0x013d, 0x030c, 0x004c, 0, // Lcaron, dead_caron, L
+ 0x015a, 0x0301, 0x0053, 0, // Sacute, dead_acute, S
+ 0x0160, 0x030c, 0x0053, 0, // Scaron, dead_caron, S
+ 0x015e, 0x0327, 0x0053, 0, // Scedilla, dead_cedilla, S
+ 0x0164, 0x030c, 0x0054, 0, // Tcaron, dead_caron, T
+ 0x0179, 0x0301, 0x005a, 0, // Zacute, dead_acute, Z
+ 0x017d, 0x030c, 0x005a, 0, // Zcaron, dead_caron, Z
+ 0x017b, 0x0307, 0x005a, 0, // Zabovedot, dead_abovedot, Z
+ 0x0105, 0x0328, 0x0061, 0, // aogonek, dead_ogonek, a
+ 0x013e, 0x030c, 0x006c, 0, // lcaron, dead_caron, l
+ 0x015b, 0x0301, 0x0073, 0, // sacute, dead_acute, s
+ 0x0161, 0x030c, 0x0073, 0, // scaron, dead_caron, s
+ 0x015f, 0x0327, 0x0073, 0, // scedilla, dead_cedilla, s
+ 0x0165, 0x030c, 0x0074, 0, // tcaron, dead_caron, t
+ 0x017a, 0x0301, 0x007a, 0, // zacute, dead_acute, z
+ 0x017e, 0x030c, 0x007a, 0, // zcaron, dead_caron, z
+ 0x017c, 0x0307, 0x007a, 0, // zabovedot, dead_abovedot, z
+ 0x0154, 0x0301, 0x0052, 0, // Racute, dead_acute, R
+ 0x0102, 0x0306, 0x0041, 0, // Abreve, dead_breve, A
+ 0x0139, 0x0301, 0x004c, 0, // Lacute, dead_acute, L
+ 0x0106, 0x0301, 0x0043, 0, // Cacute, dead_acute, C
+ 0x010c, 0x030c, 0x0043, 0, // Ccaron, dead_caron, C
+ 0x0118, 0x0328, 0x0045, 0, // Eogonek, dead_ogonek, E
+ 0x011a, 0x030c, 0x0045, 0, // Ecaron, dead_caron, E
+ 0x010e, 0x030c, 0x0044, 0, // Dcaron, dead_caron, D
+ 0x0143, 0x0301, 0x004e, 0, // Nacute, dead_acute, N
+ 0x0147, 0x030c, 0x004e, 0, // Ncaron, dead_caron, N
+ 0x0150, 0x030b, 0x004f, 0, // Odoubleacute, dead_doubleacute, O
+ 0x0158, 0x030c, 0x0052, 0, // Rcaron, dead_caron, R
+ 0x016e, 0x030a, 0x0055, 0, // Uring, dead_abovering, U
+ 0x0170, 0x030b, 0x0055, 0, // Udoubleacute, dead_doubleacute, U
+ 0x0162, 0x0327, 0x0054, 0, // Tcedilla, dead_cedilla, T
+ 0x0155, 0x0301, 0x0072, 0, // racute, dead_acute, r
+ 0x0103, 0x0306, 0x0061, 0, // abreve, dead_breve, a
+ 0x013a, 0x0301, 0x006c, 0, // lacute, dead_acute, l
+ 0x0107, 0x0301, 0x0063, 0, // cacute, dead_acute, c
+ 0x010d, 0x030c, 0x0063, 0, // ccaron, dead_caron, c
+ 0x0119, 0x0328, 0x0065, 0, // eogonek, dead_ogonek, e
+ 0x011b, 0x030c, 0x0065, 0, // ecaron, dead_caron, e
+ 0x010f, 0x030c, 0x0064, 0, // dcaron, dead_caron, d
+ 0x0144, 0x0301, 0x006e, 0, // nacute, dead_acute, n
+ 0x0148, 0x030c, 0x006e, 0, // ncaron, dead_caron, n
+ 0x0151, 0x030b, 0x006f, 0, // odoubleacute, dead_doubleacute, o
+ 0x0159, 0x030c, 0x0072, 0, // rcaron, dead_caron, r
+ 0x016f, 0x030a, 0x0075, 0, // uring, dead_abovering, u
+ 0x0171, 0x030b, 0x0075, 0, // udoubleacute, dead_doubleacute, u
+ 0x0163, 0x0327, 0x0074, 0, // tcedilla, dead_cedilla, t
+
+ // Latin-3 (ISO 8859-3)
+ 0x0124, 0x0302, 0x0048, 0, // Hcircumflex, dead_circumflex, H
+ 0x0130, 0x0307, 0x0049, 0, // Iabovedot, dead_abovedot, I
+ 0x011e, 0x0306, 0x0047, 0, // Gbreve, dead_breve, G
+ 0x0134, 0x0302, 0x004a, 0, // Jcircumflex, dead_circumflex, J
+ 0x0125, 0x0302, 0x0068, 0, // hcircumflex, dead_circumflex, h
+ 0x011f, 0x0306, 0x0067, 0, // gbreve, dead_breve, g
+ 0x0135, 0x0302, 0x006a, 0, // jcircumflex, dead_circumflex, j
+ 0x010a, 0x0307, 0x0043, 0, // Cabovedot, dead_abovedot, C
+ 0x0108, 0x0302, 0x0043, 0, // Ccircumflex, dead_circumflex, C
+ 0x0120, 0x0307, 0x0047, 0, // Gabovedot, dead_abovedot, G
+ 0x011c, 0x0302, 0x0047, 0, // Gcircumflex, dead_circumflex, G
+ 0x016c, 0x0306, 0x0055, 0, // Ubreve, dead_breve, U
+ 0x015c, 0x0302, 0x0053, 0, // Scircumflex, dead_circumflex, S
+ 0x010b, 0x0307, 0x0063, 0, // cabovedot, dead_abovedot, c
+ 0x0109, 0x0302, 0x0063, 0, // ccircumflex, dead_circumflex, c
+ 0x0121, 0x0307, 0x0067, 0, // gabovedot, dead_abovedot, g
+ 0x011d, 0x0302, 0x0067, 0, // gcircumflex, dead_circumflex, g
+ 0x016d, 0x0306, 0x0075, 0, // ubreve, dead_breve, u
+ 0x015d, 0x0302, 0x0073, 0, // scircumflex, dead_circumflex, s
+
+ // Latin-4 (ISO 8859-4)
+ 0x0156, 0x0327, 0x0052, 0, // Rcedilla, dead_cedilla, R
+ 0x0128, 0x0303, 0x0049, 0, // Itilde, dead_tilde, I
+ 0x013b, 0x0327, 0x004c, 0, // Lcedilla, dead_cedilla, L
+ 0x0112, 0x0304, 0x0045, 0, // Emacron, dead_macron, E
+ 0x0122, 0x0327, 0x0047, 0, // Gcedilla, dead_cedilla, G
+ 0x0157, 0x0327, 0x0072, 0, // rcedilla, dead_cedilla, r
+ 0x0129, 0x0303, 0x0069, 0, // itilde, dead_tilde, i
+ 0x013c, 0x0327, 0x006c, 0, // lcedilla, dead_cedilla, l
+ 0x0113, 0x0304, 0x0065, 0, // emacron, dead_macron, e
+ 0x0123, 0x0327, 0x0067, 0, // gcedilla, dead_cedilla, g
+ 0x0100, 0x0304, 0x0041, 0, // Amacron, dead_macron, A
+ 0x012e, 0x0328, 0x0049, 0, // Iogonek, dead_ogonek, I
+ 0x0116, 0x0307, 0x0045, 0, // Eabovedot, dead_abovedot, E
+ 0x012a, 0x0304, 0x0049, 0, // Imacron, dead_macron, I
+ 0x0145, 0x0327, 0x004e, 0, // Ncedilla, dead_cedilla, N
+ 0x014c, 0x0304, 0x004f, 0, // Omacron, dead_macron, O
+ 0x0136, 0x0327, 0x004b, 0, // Kcedilla, dead_cedilla, K
+ 0x0172, 0x0328, 0x0055, 0, // Uogonek, dead_ogonek, U
+ 0x0168, 0x0303, 0x0055, 0, // Utilde, dead_tilde, U
+ 0x016a, 0x0304, 0x0055, 0, // Umacron, dead_macron, U
+ 0x0101, 0x0304, 0x0061, 0, // amacron, dead_macron, a
+ 0x012f, 0x0328, 0x0069, 0, // iogonek, dead_ogonek, i
+ 0x0117, 0x0307, 0x0065, 0, // eabovedot, dead_abovedot, e
+ 0x012b, 0x0304, 0x0069, 0, // imacron, dead_macron, i
+ 0x0146, 0x0327, 0x006e, 0, // ncedilla, dead_cedilla, n
+ 0x014d, 0x0304, 0x006f, 0, // omacron, dead_macron, o
+ 0x0137, 0x0327, 0x006b, 0, // kcedilla, dead_cedilla, k
+ 0x0173, 0x0328, 0x0075, 0, // uogonek, dead_ogonek, u
+ 0x0169, 0x0303, 0x0075, 0, // utilde, dead_tilde, u
+ 0x016b, 0x0304, 0x0075, 0, // umacron, dead_macron, u
+
+ // Latin-8 (ISO 8859-14)
+ 0x1e02, 0x0307, 0x0042, 0, // Babovedot, dead_abovedot, B
+ 0x1e03, 0x0307, 0x0062, 0, // babovedot, dead_abovedot, b
+ 0x1e0a, 0x0307, 0x0044, 0, // Dabovedot, dead_abovedot, D
+ 0x1e80, 0x0300, 0x0057, 0, // Wgrave, dead_grave, W
+ 0x1e82, 0x0301, 0x0057, 0, // Wacute, dead_acute, W
+ 0x1e0b, 0x0307, 0x0064, 0, // dabovedot, dead_abovedot, d
+ 0x1ef2, 0x0300, 0x0059, 0, // Ygrave, dead_grave, Y
+ 0x1e1e, 0x0307, 0x0046, 0, // Fabovedot, dead_abovedot, F
+ 0x1e1f, 0x0307, 0x0066, 0, // fabovedot, dead_abovedot, f
+ 0x1e40, 0x0307, 0x004d, 0, // Mabovedot, dead_abovedot, M
+ 0x1e41, 0x0307, 0x006d, 0, // mabovedot, dead_abovedot, m
+ 0x1e56, 0x0307, 0x0050, 0, // Pabovedot, dead_abovedot, P
+ 0x1e81, 0x0300, 0x0077, 0, // wgrave, dead_grave, w
+ 0x1e57, 0x0307, 0x0070, 0, // pabovedot, dead_abovedot, p
+ 0x1e83, 0x0301, 0x0077, 0, // wacute, dead_acute, w
+ 0x1e60, 0x0307, 0x0053, 0, // Sabovedot, dead_abovedot, S
+ 0x1ef3, 0x0300, 0x0079, 0, // ygrave, dead_grave, y
+ 0x1e84, 0x0308, 0x0057, 0, // Wdiaeresis, dead_diaeresis, W
+ 0x1e85, 0x0308, 0x0077, 0, // wdiaeresis, dead_diaeresis, w
+ 0x1e61, 0x0307, 0x0073, 0, // sabovedot, dead_abovedot, s
+ 0x0174, 0x0302, 0x0057, 0, // Wcircumflex, dead_circumflex, W
+ 0x1e6a, 0x0307, 0x0054, 0, // Tabovedot, dead_abovedot, T
+ 0x0176, 0x0302, 0x0059, 0, // Ycircumflex, dead_circumflex, Y
+ 0x0175, 0x0302, 0x0077, 0, // wcircumflex, dead_circumflex, w
+ 0x1e6b, 0x0307, 0x0074, 0, // tabovedot, dead_abovedot, t
+ 0x0177, 0x0302, 0x0079, 0, // ycircumflex, dead_circumflex, y
+
+ // Latin-9 (ISO 8859-15)
+ 0x0178, 0x0308, 0x0059, 0, // Ydiaeresis, dead_diaeresis, Y
+
+ // Compose key sequences
+ 0x00c6, kKeyCompose, 0x0041, 0x0045, 0, // AE, A, E
+ 0x00c1, kKeyCompose, 0x0041, 0x0027, 0, // Aacute, A, apostrophe
+ 0x00c2, kKeyCompose, 0x0041, 0x0053, 0, // Acircumflex, A, asciicircum
+ 0x00c3, kKeyCompose, 0x0041, 0x0022, 0, // Adiaeresis, A, quotedbl
+ 0x00c0, kKeyCompose, 0x0041, 0x0060, 0, // Agrave, A, grave
+ 0x00c5, kKeyCompose, 0x0041, 0x002a, 0, // Aring, A, asterisk
+ 0x00c3, kKeyCompose, 0x0041, 0x007e, 0, // Atilde, A, asciitilde
+ 0x00c7, kKeyCompose, 0x0043, 0x002c, 0, // Ccedilla, C, comma
+ 0x00d0, kKeyCompose, 0x0044, 0x002d, 0, // ETH, D, minus
+ 0x00c9, kKeyCompose, 0x0045, 0x0027, 0, // Eacute, E, apostrophe
+ 0x00ca, kKeyCompose, 0x0045, 0x0053, 0, // Ecircumflex, E, asciicircum
+ 0x00cb, kKeyCompose, 0x0045, 0x0022, 0, // Ediaeresis, E, quotedbl
+ 0x00c8, kKeyCompose, 0x0045, 0x0060, 0, // Egrave, E, grave
+ 0x00cd, kKeyCompose, 0x0049, 0x0027, 0, // Iacute, I, apostrophe
+ 0x00ce, kKeyCompose, 0x0049, 0x0053, 0, // Icircumflex, I, asciicircum
+ 0x00cf, kKeyCompose, 0x0049, 0x0022, 0, // Idiaeresis, I, quotedbl
+ 0x00cc, kKeyCompose, 0x0049, 0x0060, 0, // Igrave, I, grave
+ 0x00d1, kKeyCompose, 0x004e, 0x007e, 0, // Ntilde, N, asciitilde
+ 0x00d3, kKeyCompose, 0x004f, 0x0027, 0, // Oacute, O, apostrophe
+ 0x00d4, kKeyCompose, 0x004f, 0x0053, 0, // Ocircumflex, O, asciicircum
+ 0x00d6, kKeyCompose, 0x004f, 0x0022, 0, // Odiaeresis, O, quotedbl
+ 0x00d2, kKeyCompose, 0x004f, 0x0060, 0, // Ograve, O, grave
+ 0x00d8, kKeyCompose, 0x004f, 0x002f, 0, // Ooblique, O, slash
+ 0x00d5, kKeyCompose, 0x004f, 0x007e, 0, // Otilde, O, asciitilde
+ 0x00de, kKeyCompose, 0x0054, 0x0048, 0, // THORN, T, H
+ 0x00da, kKeyCompose, 0x0055, 0x0027, 0, // Uacute, U, apostrophe
+ 0x00db, kKeyCompose, 0x0055, 0x0053, 0, // Ucircumflex, U, asciicircum
+ 0x00dc, kKeyCompose, 0x0055, 0x0022, 0, // Udiaeresis, U, quotedbl
+ 0x00d9, kKeyCompose, 0x0055, 0x0060, 0, // Ugrave, U, grave
+ 0x00dd, kKeyCompose, 0x0059, 0x0027, 0, // Yacute, Y, apostrophe
+ 0x00e1, kKeyCompose, 0x0061, 0x0027, 0, // aacute, a, apostrophe
+ 0x00e2, kKeyCompose, 0x0061, 0x0053, 0, // acircumflex, a, asciicircum
+ 0x00b4, kKeyCompose, 0x0027, 0x0027, 0, // acute, apostrophe, apostrophe
+ 0x00e4, kKeyCompose, 0x0061, 0x0022, 0, // adiaeresis, a, quotedbl
+ 0x00e6, kKeyCompose, 0x0061, 0x0065, 0, // ae, a, e
+ 0x00e0, kKeyCompose, 0x0061, 0x0060, 0, // agrave, a, grave
+ 0x00e5, kKeyCompose, 0x0061, 0x002a, 0, // aring, a, asterisk
+ 0x0040, kKeyCompose, 0x0041, 0x0054, 0, // at, A, T
+ 0x00e3, kKeyCompose, 0x0061, 0x007e, 0, // atilde, a, asciitilde
+ 0x005c, kKeyCompose, 0x002f, 0x002f, 0, // backslash, slash, slash
+ 0x007c, kKeyCompose, 0x004c, 0x0056, 0, // bar, L, V
+ 0x007b, kKeyCompose, 0x0028, 0x002d, 0, // braceleft, parenleft, minus
+ 0x007d, kKeyCompose, 0x0029, 0x002d, 0, // braceright, parenright, minus
+ 0x005b, kKeyCompose, 0x0028, 0x0028, 0, // bracketleft, parenleft, parenleft
+ 0x005d, kKeyCompose, 0x0029, 0x0029, 0, // bracketright, parenright, parenright
+ 0x00a6, kKeyCompose, 0x0042, 0x0056, 0, // brokenbar, B, V
+ 0x00e7, kKeyCompose, 0x0063, 0x002c, 0, // ccedilla, c, comma
+ 0x00b8, kKeyCompose, 0x002c, 0x002c, 0, // cedilla, comma, comma
+ 0x00a2, kKeyCompose, 0x0063, 0x002f, 0, // cent, c, slash
+ 0x00a9, kKeyCompose, 0x0028, 0x0063, 0, // copyright, parenleft, c
+ 0x00a4, kKeyCompose, 0x006f, 0x0078, 0, // currency, o, x
+ 0x00b0, kKeyCompose, 0x0030, 0x0053, 0, // degree, 0, asciicircum
+ 0x00a8, kKeyCompose, 0x0022, 0x0022, 0, // diaeresis, quotedbl, quotedbl
+ 0x00f7, kKeyCompose, 0x003a, 0x002d, 0, // division, colon, minus
+ 0x00e9, kKeyCompose, 0x0065, 0x0027, 0, // eacute, e, apostrophe
+ 0x00ea, kKeyCompose, 0x0065, 0x0053, 0, // ecircumflex, e, asciicircum
+ 0x00eb, kKeyCompose, 0x0065, 0x0022, 0, // ediaeresis, e, quotedbl
+ 0x00e8, kKeyCompose, 0x0065, 0x0060, 0, // egrave, e, grave
+ 0x00f0, kKeyCompose, 0x0064, 0x002d, 0, // eth, d, minus
+ 0x00a1, kKeyCompose, 0x0021, 0x0021, 0, // exclamdown, exclam, exclam
+ 0x00ab, kKeyCompose, 0x003c, 0x003c, 0, // guillemotleft, less, less
+ 0x00bb, kKeyCompose, 0x003e, 0x003e, 0, // guillemotright, greater, greater
+ 0x0023, kKeyCompose, 0x002b, 0x002b, 0, // numbersign, plus, plus
+ 0x00ad, kKeyCompose, 0x002d, 0x002d, 0, // hyphen, minus, minus
+ 0x00ed, kKeyCompose, 0x0069, 0x0027, 0, // iacute, i, apostrophe
+ 0x00ee, kKeyCompose, 0x0069, 0x0053, 0, // icircumflex, i, asciicircum
+ 0x00ef, kKeyCompose, 0x0069, 0x0022, 0, // idiaeresis, i, quotedbl
+ 0x00ec, kKeyCompose, 0x0069, 0x0060, 0, // igrave, i, grave
+ 0x00af, kKeyCompose, 0x002d, 0x0053, 0, // macron, minus, asciicircum
+ 0x00ba, kKeyCompose, 0x006f, 0x005f, 0, // masculine, o, underscore
+ 0x00b5, kKeyCompose, 0x0075, 0x002f, 0, // mu, u, slash
+ 0x00d7, kKeyCompose, 0x0078, 0x0078, 0, // multiply, x, x
+ 0x00a0, kKeyCompose, 0x0020, 0x0020, 0, // nobreakspace, space, space
+ 0x00ac, kKeyCompose, 0x002c, 0x002d, 0, // notsign, comma, minus
+ 0x00f1, kKeyCompose, 0x006e, 0x007e, 0, // ntilde, n, asciitilde
+ 0x00f3, kKeyCompose, 0x006f, 0x0027, 0, // oacute, o, apostrophe
+ 0x00f4, kKeyCompose, 0x006f, 0x0053, 0, // ocircumflex, o, asciicircum
+ 0x00f6, kKeyCompose, 0x006f, 0x0022, 0, // odiaeresis, o, quotedbl
+ 0x00f2, kKeyCompose, 0x006f, 0x0060, 0, // ograve, o, grave
+ 0x00bd, kKeyCompose, 0x0031, 0x0032, 0, // onehalf, 1, 2
+ 0x00bc, kKeyCompose, 0x0031, 0x0034, 0, // onequarter, 1, 4
+ 0x00b9, kKeyCompose, 0x0031, 0x0053, 0, // onesuperior, 1, asciicircum
+ 0x00aa, kKeyCompose, 0x0061, 0x005f, 0, // ordfeminine, a, underscore
+ 0x00f8, kKeyCompose, 0x006f, 0x002f, 0, // oslash, o, slash
+ 0x00f5, kKeyCompose, 0x006f, 0x007e, 0, // otilde, o, asciitilde
+ 0x00b6, kKeyCompose, 0x0070, 0x0021, 0, // paragraph, p, exclam
+ 0x00b7, kKeyCompose, 0x002e, 0x002e, 0, // periodcentered, period, period
+ 0x00b1, kKeyCompose, 0x002b, 0x002d, 0, // plusminus, plus, minus
+ 0x00bf, kKeyCompose, 0x003f, 0x003f, 0, // questiondown, question, question
+ 0x00ae, kKeyCompose, 0x0028, 0x0072, 0, // registered, parenleft, r
+ 0x00a7, kKeyCompose, 0x0073, 0x006f, 0, // section, s, o
+ 0x00df, kKeyCompose, 0x0073, 0x0073, 0, // ssharp, s, s
+ 0x00a3, kKeyCompose, 0x004c, 0x002d, 0, // sterling, L, minus
+ 0x00fe, kKeyCompose, 0x0074, 0x0068, 0, // thorn, t, h
+ 0x00be, kKeyCompose, 0x0033, 0x0034, 0, // threequarters, 3, 4
+ 0x00b3, kKeyCompose, 0x0033, 0x0053, 0, // threesuperior, 3, asciicircum
+ 0x00b2, kKeyCompose, 0x0032, 0x0053, 0, // twosuperior, 2, asciicircum
+ 0x00fa, kKeyCompose, 0x0075, 0x0027, 0, // uacute, u, apostrophe
+ 0x00fb, kKeyCompose, 0x0075, 0x0053, 0, // ucircumflex, u, asciicircum
+ 0x00fc, kKeyCompose, 0x0075, 0x0022, 0, // udiaeresis, u, quotedbl
+ 0x00f9, kKeyCompose, 0x0075, 0x0060, 0, // ugrave, u, grave
+ 0x00fd, kKeyCompose, 0x0079, 0x0027, 0, // yacute, y, apostrophe
+ 0x00ff, kKeyCompose, 0x0079, 0x0022, 0, // ydiaeresis, y, quotedbl
+ 0x00a5, kKeyCompose, 0x0079, 0x003d, 0, // yen, y, equal
+
+ // end of table
+ 0
+};
+
+static const KeyID s_numpadTable[] = {
+ kKeyKP_Space, 0x0020,
+ kKeyKP_Tab, kKeyTab,
+ kKeyKP_Enter, kKeyReturn,
+ kKeyKP_F1, kKeyF1,
+ kKeyKP_F2, kKeyF2,
+ kKeyKP_F3, kKeyF3,
+ kKeyKP_F4, kKeyF4,
+ kKeyKP_Home, kKeyHome,
+ kKeyKP_Left, kKeyLeft,
+ kKeyKP_Up, kKeyUp,
+ kKeyKP_Right, kKeyRight,
+ kKeyKP_Down, kKeyDown,
+ kKeyKP_PageUp, kKeyPageUp,
+ kKeyKP_PageDown, kKeyPageDown,
+ kKeyKP_End, kKeyEnd,
+ kKeyKP_Begin, kKeyBegin,
+ kKeyKP_Insert, kKeyInsert,
+ kKeyKP_Delete, kKeyDelete,
+ kKeyKP_Equal, 0x003d,
+ kKeyKP_Multiply, 0x002a,
+ kKeyKP_Add, 0x002b,
+ kKeyKP_Separator, 0x002c,
+ kKeyKP_Subtract, 0x002d,
+ kKeyKP_Decimal, 0x002e,
+ kKeyKP_Divide, 0x002f,
+ kKeyKP_0, 0x0030,
+ kKeyKP_1, 0x0031,
+ kKeyKP_2, 0x0032,
+ kKeyKP_3, 0x0033,
+ kKeyKP_4, 0x0034,
+ kKeyKP_5, 0x0035,
+ kKeyKP_6, 0x0036,
+ kKeyKP_7, 0x0037,
+ kKeyKP_8, 0x0038,
+ kKeyKP_9, 0x0039
+};
+
+//
+// KeyState
+//
+
+KeyState::KeyState(IEventQueue* events) :
+ IKeyState(events),
+ m_keyMapPtr(new barrier::KeyMap()),
+ m_keyMap(*m_keyMapPtr),
+ m_mask(0),
+ m_events(events)
+{
+ init();
+}
+
+KeyState::KeyState(IEventQueue* events, barrier::KeyMap& keyMap) :
+ IKeyState(events),
+ m_keyMapPtr(0),
+ m_keyMap(keyMap),
+ m_mask(0),
+ m_events(events)
+{
+ init();
+}
+
+KeyState::~KeyState()
+{
+ if (m_keyMapPtr)
+ delete m_keyMapPtr;
+}
+
+void
+KeyState::init()
+{
+ memset(&m_keys, 0, sizeof(m_keys));
+ memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys));
+ memset(&m_keyClientData, 0, sizeof(m_keyClientData));
+ memset(&m_serverKeys, 0, sizeof(m_serverKeys));
+}
+
+void
+KeyState::onKey(KeyButton button, bool down, KeyModifierMask newState)
+{
+ // update modifier state
+ m_mask = newState;
+ LOG((CLOG_DEBUG1 "new mask: 0x%04x", m_mask));
+
+ // ignore bogus buttons
+ button &= kButtonMask;
+ if (button == 0) {
+ return;
+ }
+
+ // update key state
+ if (down) {
+ m_keys[button] = 1;
+ m_syntheticKeys[button] = 1;
+ }
+ else {
+ m_keys[button] = 0;
+ m_syntheticKeys[button] = 0;
+ }
+}
+
+void
+KeyState::sendKeyEvent(
+ void* target, bool press, bool isAutoRepeat,
+ KeyID key, KeyModifierMask mask,
+ SInt32 count, KeyButton button)
+{
+ if (m_keyMap.isHalfDuplex(key, button)) {
+ if (isAutoRepeat) {
+ // ignore auto-repeat on half-duplex keys
+ }
+ else {
+ m_events->addEvent(Event(m_events->forIKeyState().keyDown(), target,
+ KeyInfo::alloc(key, mask, button, 1)));
+ m_events->addEvent(Event(m_events->forIKeyState().keyUp(), target,
+ KeyInfo::alloc(key, mask, button, 1)));
+ }
+ }
+ else {
+ if (isAutoRepeat) {
+ m_events->addEvent(Event(m_events->forIKeyState().keyRepeat(), target,
+ KeyInfo::alloc(key, mask, button, count)));
+ }
+ else if (press) {
+ m_events->addEvent(Event(m_events->forIKeyState().keyDown(), target,
+ KeyInfo::alloc(key, mask, button, 1)));
+ }
+ else {
+ m_events->addEvent(Event(m_events->forIKeyState().keyUp(), target,
+ KeyInfo::alloc(key, mask, button, 1)));
+ }
+ }
+}
+
+void
+KeyState::updateKeyMap()
+{
+ // get the current keyboard map
+ barrier::KeyMap keyMap;
+ getKeyMap(keyMap);
+ m_keyMap.swap(keyMap);
+ m_keyMap.finish();
+
+ // add special keys
+ addCombinationEntries();
+ addKeypadEntries();
+ addAliasEntries();
+}
+
+void
+KeyState::updateKeyState()
+{
+ // reset our state
+ memset(&m_keys, 0, sizeof(m_keys));
+ memset(&m_syntheticKeys, 0, sizeof(m_syntheticKeys));
+ memset(&m_keyClientData, 0, sizeof(m_keyClientData));
+ memset(&m_serverKeys, 0, sizeof(m_serverKeys));
+ m_activeModifiers.clear();
+
+ // get the current keyboard state
+ KeyButtonSet keysDown;
+ pollPressedKeys(keysDown);
+ for (KeyButtonSet::const_iterator i = keysDown.begin();
+ i != keysDown.end(); ++i) {
+ m_keys[*i] = 1;
+ }
+
+ // get the current modifier state
+ m_mask = pollActiveModifiers();
+
+ // set active modifiers
+ AddActiveModifierContext addModifierContext(pollActiveGroup(), m_mask,
+ m_activeModifiers);
+ m_keyMap.foreachKey(&KeyState::addActiveModifierCB, &addModifierContext);
+
+ LOG((CLOG_DEBUG1 "modifiers on update: 0x%04x", m_mask));
+}
+
+void
+KeyState::addActiveModifierCB(KeyID, SInt32 group,
+ barrier::KeyMap::KeyItem& keyItem, void* vcontext)
+{
+ AddActiveModifierContext* context =
+ static_cast<AddActiveModifierContext*>(vcontext);
+ if (group == context->m_activeGroup &&
+ (keyItem.m_generates & context->m_mask) != 0) {
+ context->m_activeModifiers.insert(std::make_pair(
+ keyItem.m_generates, keyItem));
+ }
+}
+
+void
+KeyState::setHalfDuplexMask(KeyModifierMask mask)
+{
+ m_keyMap.clearHalfDuplexModifiers();
+ if ((mask & KeyModifierCapsLock) != 0) {
+ m_keyMap.addHalfDuplexModifier(kKeyCapsLock);
+ }
+ if ((mask & KeyModifierNumLock) != 0) {
+ m_keyMap.addHalfDuplexModifier(kKeyNumLock);
+ }
+ if ((mask & KeyModifierScrollLock) != 0) {
+ m_keyMap.addHalfDuplexModifier(kKeyScrollLock);
+ }
+}
+
+void
+KeyState::fakeKeyDown(KeyID id, KeyModifierMask mask, KeyButton serverID)
+{
+ // 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);
+ return;
+ }
+
+ // ignore certain keys
+ if (isIgnoredKey(id, mask)) {
+ LOG((CLOG_DEBUG1 "ignored key %04x %04x", id, mask));
+ return;
+ }
+
+ // get keys for key press
+ Keystrokes keys;
+ ModifierToKeys oldActiveModifiers = m_activeModifiers;
+ const barrier::KeyMap::KeyItem* keyItem =
+ m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
+ getActiveModifiersRValue(), mask, false);
+ if (keyItem == NULL) {
+ // a media key won't be mapped on mac, so we need to fake it in a
+ // special way
+ if (id == kKeyAudioDown || id == kKeyAudioUp ||
+ id == kKeyAudioMute || id == kKeyAudioPlay ||
+ id == kKeyAudioPrev || id == kKeyAudioNext ||
+ id == kKeyBrightnessDown || id == kKeyBrightnessUp
+ ) {
+ LOG((CLOG_DEBUG1 "emulating media key"));
+ fakeMediaKey(id);
+ }
+
+ return;
+ }
+
+ KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask);
+ updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers);
+ if (localID != 0) {
+ // note keys down
+ ++m_keys[localID];
+ ++m_syntheticKeys[localID];
+ m_keyClientData[localID] = keyItem->m_client;
+ m_serverKeys[serverID] = localID;
+ }
+
+ // generate key events
+ fakeKeys(keys, 1);
+}
+
+bool
+KeyState::fakeKeyRepeat(
+ KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton serverID)
+{
+ serverID &= kButtonMask;
+
+ // if we haven't seen this button go down then ignore it
+ KeyButton oldLocalID = m_serverKeys[serverID];
+ if (oldLocalID == 0) {
+ return false;
+ }
+
+ // get keys for key repeat
+ Keystrokes keys;
+ ModifierToKeys oldActiveModifiers = m_activeModifiers;
+ const barrier::KeyMap::KeyItem* keyItem =
+ m_keyMap.mapKey(keys, id, pollActiveGroup(), m_activeModifiers,
+ getActiveModifiersRValue(), mask, true);
+ if (keyItem == NULL) {
+ return false;
+ }
+ KeyButton localID = (KeyButton)(keyItem->m_button & kButtonMask);
+ if (localID == 0) {
+ return false;
+ }
+
+ // if the KeyButton for the auto-repeat is not the same as for the
+ // initial press then mark the initial key as released and the new
+ // key as pressed. this can happen when we auto-repeat after a
+ // dead key. for example, a dead accent followed by 'a' will
+ // generate an 'a with accent' followed by a repeating 'a'. the
+ // KeyButtons for the two KeyIDs might be different.
+ if (localID != oldLocalID) {
+ // replace key up with previous KeyButton but leave key down
+ // alone so it uses the new KeyButton.
+ for (Keystrokes::iterator index = keys.begin();
+ index != keys.end(); ++index) {
+ if (index->m_type == Keystroke::kButton &&
+ index->m_data.m_button.m_button == localID) {
+ index->m_data.m_button.m_button = oldLocalID;
+ break;
+ }
+ }
+
+ // note that old key is now up
+ --m_keys[oldLocalID];
+ --m_syntheticKeys[oldLocalID];
+
+ // note keys down
+ updateModifierKeyState(localID, oldActiveModifiers, m_activeModifiers);
+ ++m_keys[localID];
+ ++m_syntheticKeys[localID];
+ m_keyClientData[localID] = keyItem->m_client;
+ m_serverKeys[serverID] = localID;
+ }
+
+ // generate key events
+ fakeKeys(keys, count);
+ return true;
+}
+
+bool
+KeyState::fakeKeyUp(KeyButton serverID)
+{
+ // if we haven't seen this button go down then ignore it
+ KeyButton localID = m_serverKeys[serverID & kButtonMask];
+ if (localID == 0) {
+ return false;
+ }
+
+ // get the sequence of keys to simulate key release
+ Keystrokes keys;
+ keys.push_back(Keystroke(localID, false, false, m_keyClientData[localID]));
+
+ // note keys down
+ --m_keys[localID];
+ --m_syntheticKeys[localID];
+ m_serverKeys[serverID] = 0;
+
+ // check if this is a modifier
+ ModifierToKeys::iterator i = m_activeModifiers.begin();
+ while (i != m_activeModifiers.end()) {
+ if (i->second.m_button == localID && !i->second.m_lock) {
+ // modifier is no longer down
+ KeyModifierMask mask = i->first;
+
+ ModifierToKeys::iterator tmp = i;
+ ++i;
+ m_activeModifiers.erase(tmp);
+
+ if (m_activeModifiers.count(mask) == 0) {
+ // no key for modifier is down so deactivate modifier
+ m_mask &= ~mask;
+ LOG((CLOG_DEBUG1 "new state %04x", m_mask));
+ }
+ }
+ else {
+ ++i;
+ }
+ }
+
+ // generate key events
+ fakeKeys(keys, 1);
+ return true;
+}
+
+void
+KeyState::fakeAllKeysUp()
+{
+ Keystrokes keys;
+ for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
+ if (m_syntheticKeys[i] > 0) {
+ keys.push_back(Keystroke(i, false, false, m_keyClientData[i]));
+ m_keys[i] = 0;
+ m_syntheticKeys[i] = 0;
+ }
+ }
+ fakeKeys(keys, 1);
+ memset(&m_serverKeys, 0, sizeof(m_serverKeys));
+ m_activeModifiers.clear();
+ m_mask = pollActiveModifiers();
+}
+
+bool
+KeyState::fakeMediaKey(KeyID id)
+{
+ return false;
+}
+
+bool
+KeyState::isKeyDown(KeyButton button) const
+{
+ return (m_keys[button & kButtonMask] > 0);
+}
+
+KeyModifierMask
+KeyState::getActiveModifiers() const
+{
+ return m_mask;
+}
+
+KeyModifierMask&
+KeyState::getActiveModifiersRValue()
+{
+ return m_mask;
+}
+
+SInt32
+KeyState::getEffectiveGroup(SInt32 group, SInt32 offset) const
+{
+ return m_keyMap.getEffectiveGroup(group, offset);
+}
+
+bool
+KeyState::isIgnoredKey(KeyID key, KeyModifierMask) const
+{
+ switch (key) {
+ case kKeyCapsLock:
+ case kKeyNumLock:
+ case kKeyScrollLock:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+KeyButton
+KeyState::getButton(KeyID id, SInt32 group) const
+{
+ const barrier::KeyMap::KeyItemList* items =
+ m_keyMap.findCompatibleKey(id, group, 0, 0);
+ if (items == NULL) {
+ return 0;
+ }
+ else {
+ return items->back().m_button;
+ }
+}
+
+void
+KeyState::addAliasEntries()
+{
+ for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
+ // if we can't shift any kKeyTab key in a particular group but we can
+ // shift kKeyLeftTab then add a shifted kKeyTab entry that matches a
+ // shifted kKeyLeftTab entry.
+ m_keyMap.addKeyAliasEntry(kKeyTab, g,
+ KeyModifierShift, KeyModifierShift,
+ kKeyLeftTab,
+ KeyModifierShift, KeyModifierShift);
+
+ // if we have no kKeyLeftTab but we do have a kKeyTab that can be
+ // shifted then add kKeyLeftTab that matches a kKeyTab.
+ m_keyMap.addKeyAliasEntry(kKeyLeftTab, g,
+ KeyModifierShift, KeyModifierShift,
+ kKeyTab,
+ 0, KeyModifierShift);
+
+ // map non-breaking space to space
+ m_keyMap.addKeyAliasEntry(0x20, g, 0, 0, 0xa0, 0, 0);
+ }
+}
+
+void
+KeyState::addKeypadEntries()
+{
+ // map every numpad key to its equivalent non-numpad key if it's not
+ // on the keyboard.
+ for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
+ for (size_t i = 0; i < sizeof(s_numpadTable) /
+ sizeof(s_numpadTable[0]); i += 2) {
+ m_keyMap.addKeyCombinationEntry(s_numpadTable[i], g,
+ s_numpadTable + i + 1, 1);
+ }
+ }
+}
+
+void
+KeyState::addCombinationEntries()
+{
+ for (SInt32 g = 0, n = m_keyMap.getNumGroups(); g < n; ++g) {
+ // add dead and compose key composition sequences
+ for (const KeyID* i = s_decomposeTable; *i != 0; ++i) {
+ // count the decomposed keys for this key
+ UInt32 numKeys = 0;
+ for (const KeyID* j = i; *++j != 0; ) {
+ ++numKeys;
+ }
+
+ // add an entry for this key
+ m_keyMap.addKeyCombinationEntry(*i, g, i + 1, numKeys);
+
+ // next key
+ i += numKeys + 1;
+ }
+ }
+}
+
+void
+KeyState::fakeKeys(const Keystrokes& keys, UInt32 count)
+{
+ // do nothing if no keys or no repeats
+ if (count == 0 || keys.empty()) {
+ return;
+ }
+
+ // generate key events
+ LOG((CLOG_DEBUG1 "keystrokes:"));
+ for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) {
+ if (k->m_type == Keystroke::kButton && k->m_data.m_button.m_repeat) {
+ // repeat from here up to but not including the next key
+ // with m_repeat == false count times.
+ Keystrokes::const_iterator start = k;
+ while (count-- > 0) {
+ // send repeating events
+ for (k = start; k != keys.end() &&
+ k->m_type == Keystroke::kButton &&
+ k->m_data.m_button.m_repeat; ++k) {
+ fakeKey(*k);
+ }
+ }
+
+ // note -- k is now on the first non-repeat key after the
+ // repeat keys, exactly where we'd like to continue from.
+ }
+ else {
+ // send event
+ fakeKey(*k);
+
+ // next key
+ ++k;
+ }
+ }
+}
+
+void
+KeyState::updateModifierKeyState(KeyButton button,
+ const ModifierToKeys& oldModifiers,
+ const ModifierToKeys& newModifiers)
+{
+ // get the pressed modifier buttons before and after
+ barrier::KeyMap::ButtonToKeyMap oldKeys, newKeys;
+ for (ModifierToKeys::const_iterator i = oldModifiers.begin();
+ i != oldModifiers.end(); ++i) {
+ oldKeys.insert(std::make_pair(i->second.m_button, &i->second));
+ }
+ for (ModifierToKeys::const_iterator i = newModifiers.begin();
+ i != newModifiers.end(); ++i) {
+ newKeys.insert(std::make_pair(i->second.m_button, &i->second));
+ }
+
+ // get the modifier buttons that were pressed or released
+ barrier::KeyMap::ButtonToKeyMap pressed, released;
+ std::set_difference(oldKeys.begin(), oldKeys.end(),
+ newKeys.begin(), newKeys.end(),
+ std::inserter(released, released.end()),
+ ButtonToKeyLess());
+ std::set_difference(newKeys.begin(), newKeys.end(),
+ oldKeys.begin(), oldKeys.end(),
+ std::inserter(pressed, pressed.end()),
+ ButtonToKeyLess());
+
+ // update state
+ for (barrier::KeyMap::ButtonToKeyMap::const_iterator i = released.begin();
+ i != released.end(); ++i) {
+ if (i->first != button) {
+ m_keys[i->first] = 0;
+ m_syntheticKeys[i->first] = 0;
+ }
+ }
+ for (barrier::KeyMap::ButtonToKeyMap::const_iterator i = pressed.begin();
+ i != pressed.end(); ++i) {
+ if (i->first != button) {
+ m_keys[i->first] = 1;
+ m_syntheticKeys[i->first] = 1;
+ m_keyClientData[i->first] = i->second->m_client;
+ }
+ }
+}
+
+//
+// KeyState::AddActiveModifierContext
+//
+
+KeyState::AddActiveModifierContext::AddActiveModifierContext(
+ SInt32 group, KeyModifierMask mask,
+ ModifierToKeys& activeModifiers) :
+ m_activeGroup(group),
+ m_mask(mask),
+ m_activeModifiers(activeModifiers)
+{
+ // do nothing
+}
diff --git a/src/lib/barrier/KeyState.h b/src/lib/barrier/KeyState.h
new file mode 100644
index 0000000..737d515
--- /dev/null
+++ b/src/lib/barrier/KeyState.h
@@ -0,0 +1,232 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/IKeyState.h"
+#include "barrier/KeyMap.h"
+
+//! Core key state
+/*!
+This class provides key state services. Subclasses must implement a few
+platform specific methods.
+*/
+class KeyState : public IKeyState {
+public:
+ KeyState(IEventQueue* events);
+ KeyState(IEventQueue* events, barrier::KeyMap& keyMap);
+ virtual ~KeyState();
+
+ //! @name manipulators
+ //@{
+
+ //! Handle key event
+ /*!
+ Sets the state of \p button to down or up and updates the current
+ modifier state to \p newState. This method should be called by
+ primary screens only in response to local events. For auto-repeat
+ set \p down to \c true. Overrides must forward to the superclass.
+ */
+ virtual void onKey(KeyButton button, bool down,
+ KeyModifierMask newState);
+
+ //! Post a key event
+ /*!
+ Posts a key event. This may adjust the event or post additional
+ events in some circumstances. If this is overridden it must forward
+ to the superclass.
+ */
+ virtual void sendKeyEvent(void* target,
+ bool press, bool isAutoRepeat,
+ KeyID key, KeyModifierMask mask,
+ SInt32 count, KeyButton button);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //@}
+
+ // IKeyState overrides
+ virtual void updateKeyMap();
+ virtual void updateKeyState();
+ virtual void setHalfDuplexMask(KeyModifierMask);
+ virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button);
+ virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button);
+ virtual bool fakeKeyUp(KeyButton button);
+ virtual void fakeAllKeysUp();
+ virtual bool fakeCtrlAltDel() = 0;
+ virtual bool fakeMediaKey(KeyID id);
+
+ virtual bool isKeyDown(KeyButton) const;
+ virtual KeyModifierMask
+ getActiveModifiers() const;
+ virtual KeyModifierMask
+ pollActiveModifiers() const = 0;
+ virtual SInt32 pollActiveGroup() const = 0;
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const = 0;
+
+ SInt32 getKeyState(KeyButton keyButton) { return m_keys[keyButton]; }
+
+protected:
+ typedef barrier::KeyMap::Keystroke Keystroke;
+
+ //! @name protected manipulators
+ //@{
+
+ //! Get the keyboard map
+ /*!
+ Fills \p keyMap with the current keyboard map.
+ */
+ virtual void getKeyMap(barrier::KeyMap& keyMap) = 0;
+
+ //! Fake a key event
+ /*!
+ Synthesize an event for \p keystroke.
+ */
+ virtual void fakeKey(const Keystroke& keystroke) = 0;
+
+ //! Get the active modifiers
+ /*!
+ Returns the modifiers that are currently active according to our
+ shadowed state. The state may be modified.
+ */
+ virtual KeyModifierMask&
+ getActiveModifiersRValue();
+
+ //@}
+ //! @name protected accessors
+ //@{
+
+ //! Compute a group number
+ /*!
+ Returns the number of the group \p offset groups after group \p group.
+ */
+ SInt32 getEffectiveGroup(SInt32 group, SInt32 offset) const;
+
+ //! Check if key is ignored
+ /*!
+ Returns \c true if and only if the key should always be ignored.
+ The default returns \c true only for the toggle keys.
+ */
+ virtual bool isIgnoredKey(KeyID key, KeyModifierMask mask) const;
+
+ //! Get button for a KeyID
+ /*!
+ Return the button mapped to key \p id in group \p group if any,
+ otherwise returns 0.
+ */
+ KeyButton getButton(KeyID id, SInt32 group) const;
+
+ //@}
+
+private:
+ typedef barrier::KeyMap::Keystrokes Keystrokes;
+ typedef barrier::KeyMap::ModifierToKeys ModifierToKeys;
+public:
+ struct AddActiveModifierContext {
+ public:
+ AddActiveModifierContext(SInt32 group, KeyModifierMask mask,
+ ModifierToKeys& activeModifiers);
+
+ public:
+ SInt32 m_activeGroup;
+ KeyModifierMask m_mask;
+ ModifierToKeys& m_activeModifiers;
+
+ private:
+ // not implemented
+ AddActiveModifierContext(const AddActiveModifierContext&);
+ AddActiveModifierContext& operator=(const AddActiveModifierContext&);
+ };
+private:
+
+ class ButtonToKeyLess {
+ public:
+ bool operator()(const barrier::KeyMap::ButtonToKeyMap::value_type& a,
+ const barrier::KeyMap::ButtonToKeyMap::value_type b) const
+ {
+ return (a.first < b.first);
+ }
+ };
+
+ // not implemented
+ KeyState(const KeyState&);
+ KeyState& operator=(const KeyState&);
+
+ // called by all ctors.
+ void init();
+
+ // adds alias key sequences. these are sequences that are equivalent
+ // to other sequences.
+ void addAliasEntries();
+
+ // adds non-keypad key sequences for keypad KeyIDs
+ void addKeypadEntries();
+
+ // adds key sequences for combination KeyIDs (those built using
+ // dead keys)
+ void addCombinationEntries();
+
+ // synthesize key events. synthesize auto-repeat events count times.
+ void fakeKeys(const Keystrokes&, UInt32 count);
+
+ // update key state to match changes to modifiers
+ void updateModifierKeyState(KeyButton button,
+ const ModifierToKeys& oldModifiers,
+ const ModifierToKeys& newModifiers);
+
+ // active modifiers collection callback
+ static void addActiveModifierCB(KeyID id, SInt32 group,
+ barrier::KeyMap::KeyItem& keyItem, void* vcontext);
+
+private:
+ // must be declared before m_keyMap. used when this class owns the key map.
+ barrier::KeyMap* m_keyMapPtr;
+
+ // the keyboard map
+ barrier::KeyMap& m_keyMap;
+
+ // current modifier state
+ KeyModifierMask m_mask;
+
+ // the active modifiers and the buttons activating them
+ ModifierToKeys m_activeModifiers;
+
+ // current keyboard state (> 0 if pressed, 0 otherwise). this is
+ // initialized to the keyboard state according to the system then
+ // it tracks synthesized events.
+ SInt32 m_keys[kNumButtons];
+
+ // synthetic keyboard state (> 0 if pressed, 0 otherwise). this
+ // tracks the synthesized keyboard state. if m_keys[n] > 0 but
+ // m_syntheticKeys[n] == 0 then the key was pressed locally and
+ // not synthesized yet.
+ SInt32 m_syntheticKeys[kNumButtons];
+
+ // client data for each pressed key
+ UInt32 m_keyClientData[kNumButtons];
+
+ // server keyboard state. an entry is 0 if not the key isn't pressed
+ // otherwise it's the local KeyButton synthesized for the server key.
+ KeyButton m_serverKeys[kNumButtons];
+
+ IEventQueue* m_events;
+};
diff --git a/src/lib/barrier/PacketStreamFilter.cpp b/src/lib/barrier/PacketStreamFilter.cpp
new file mode 100644
index 0000000..16f0fe7
--- /dev/null
+++ b/src/lib/barrier/PacketStreamFilter.cpp
@@ -0,0 +1,198 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/PacketStreamFilter.h"
+#include "base/IEventQueue.h"
+#include "mt/Lock.h"
+#include "base/TMethodEventJob.h"
+
+#include <cstring>
+#include <memory>
+
+//
+// PacketStreamFilter
+//
+
+PacketStreamFilter::PacketStreamFilter(IEventQueue* events, barrier::IStream* stream, bool adoptStream) :
+ StreamFilter(events, stream, adoptStream),
+ m_size(0),
+ m_inputShutdown(false),
+ m_events(events)
+{
+ // do nothing
+}
+
+PacketStreamFilter::~PacketStreamFilter()
+{
+ // do nothing
+}
+
+void
+PacketStreamFilter::close()
+{
+ Lock lock(&m_mutex);
+ m_size = 0;
+ m_buffer.pop(m_buffer.getSize());
+ StreamFilter::close();
+}
+
+UInt32
+PacketStreamFilter::read(void* buffer, UInt32 n)
+{
+ if (n == 0) {
+ return 0;
+ }
+
+ Lock lock(&m_mutex);
+
+ // if not enough data yet then give up
+ if (!isReadyNoLock()) {
+ return 0;
+ }
+
+ // read no more than what's left in the buffered packet
+ if (n > m_size) {
+ n = m_size;
+ }
+
+ // read it
+ if (buffer != NULL) {
+ memcpy(buffer, m_buffer.peek(n), n);
+ }
+ m_buffer.pop(n);
+ m_size -= n;
+
+ // get next packet's size if we've finished with this packet and
+ // there's enough data to do so.
+ readPacketSize();
+
+ if (m_inputShutdown && m_size == 0) {
+ m_events->addEvent(Event(m_events->forIStream().inputShutdown(),
+ getEventTarget(), NULL));
+ }
+
+ return n;
+}
+
+void
+PacketStreamFilter::write(const void* buffer, UInt32 count)
+{
+ // write the length of the payload
+ UInt8 length[4];
+ length[0] = (UInt8)((count >> 24) & 0xff);
+ length[1] = (UInt8)((count >> 16) & 0xff);
+ length[2] = (UInt8)((count >> 8) & 0xff);
+ length[3] = (UInt8)( count & 0xff);
+ getStream()->write(length, sizeof(length));
+
+ // write the payload
+ getStream()->write(buffer, count);
+}
+
+void
+PacketStreamFilter::shutdownInput()
+{
+ Lock lock(&m_mutex);
+ m_size = 0;
+ m_buffer.pop(m_buffer.getSize());
+ StreamFilter::shutdownInput();
+}
+
+bool
+PacketStreamFilter::isReady() const
+{
+ Lock lock(&m_mutex);
+ return isReadyNoLock();
+}
+
+UInt32
+PacketStreamFilter::getSize() const
+{
+ Lock lock(&m_mutex);
+ return isReadyNoLock() ? m_size : 0;
+}
+
+bool
+PacketStreamFilter::isReadyNoLock() const
+{
+ return (m_size != 0 && m_buffer.getSize() >= m_size);
+}
+
+void
+PacketStreamFilter::readPacketSize()
+{
+ // note -- m_mutex must be locked on entry
+
+ if (m_size == 0 && m_buffer.getSize() >= 4) {
+ UInt8 buffer[4];
+ memcpy(buffer, m_buffer.peek(sizeof(buffer)), sizeof(buffer));
+ m_buffer.pop(sizeof(buffer));
+ m_size = ((UInt32)buffer[0] << 24) |
+ ((UInt32)buffer[1] << 16) |
+ ((UInt32)buffer[2] << 8) |
+ (UInt32)buffer[3];
+ }
+}
+
+bool
+PacketStreamFilter::readMore()
+{
+ // note if we have whole packet
+ bool wasReady = isReadyNoLock();
+
+ // read more data
+ char buffer[4096];
+ UInt32 n = getStream()->read(buffer, sizeof(buffer));
+ while (n > 0) {
+ m_buffer.write(buffer, n);
+ n = getStream()->read(buffer, sizeof(buffer));
+ }
+
+ // if we don't yet have the next packet size then get it,
+ // if possible.
+ readPacketSize();
+
+ // note if we now have a whole packet
+ bool isReady = isReadyNoLock();
+
+ // if we weren't ready before but now we are then send a
+ // input ready event apparently from the filtered stream.
+ return (wasReady != isReady);
+}
+
+void
+PacketStreamFilter::filterEvent(const Event& event)
+{
+ if (event.getType() == m_events->forIStream().inputReady()) {
+ Lock lock(&m_mutex);
+ if (!readMore()) {
+ return;
+ }
+ }
+ else if (event.getType() == m_events->forIStream().inputShutdown()) {
+ // discard this if we have buffered data
+ Lock lock(&m_mutex);
+ m_inputShutdown = true;
+ if (m_size != 0) {
+ return;
+ }
+ }
+
+ // pass event
+ StreamFilter::filterEvent(event);
+}
diff --git a/src/lib/barrier/PacketStreamFilter.h b/src/lib/barrier/PacketStreamFilter.h
new file mode 100644
index 0000000..bcbd604
--- /dev/null
+++ b/src/lib/barrier/PacketStreamFilter.h
@@ -0,0 +1,59 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "io/StreamFilter.h"
+#include "io/StreamBuffer.h"
+#include "mt/Mutex.h"
+
+class IEventQueue;
+
+//! Packetizing stream filter
+/*!
+Filters a stream to read and write packets.
+*/
+class PacketStreamFilter : public StreamFilter {
+public:
+ PacketStreamFilter(IEventQueue* events, barrier::IStream* stream, bool adoptStream = true);
+ ~PacketStreamFilter();
+
+ // IStream overrides
+ virtual void close();
+ virtual UInt32 read(void* buffer, UInt32 n);
+ virtual void write(const void* buffer, UInt32 n);
+ virtual void shutdownInput();
+ virtual bool isReady() const;
+ virtual UInt32 getSize() const;
+
+protected:
+ // StreamFilter overrides
+ virtual void filterEvent(const Event&);
+
+private:
+ bool isReadyNoLock() const;
+ void readPacketSize();
+ bool readMore();
+
+private:
+ Mutex m_mutex;
+ UInt32 m_size;
+ StreamBuffer m_buffer;
+ bool m_inputShutdown;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/barrier/PlatformScreen.cpp b/src/lib/barrier/PlatformScreen.cpp
new file mode 100644
index 0000000..b0fdc75
--- /dev/null
+++ b/src/lib/barrier/PlatformScreen.cpp
@@ -0,0 +1,123 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/PlatformScreen.h"
+#include "barrier/App.h"
+#include "barrier/ArgsBase.h"
+
+PlatformScreen::PlatformScreen(IEventQueue* events) :
+ IPlatformScreen(events),
+ m_draggingStarted(false),
+ m_fakeDraggingStarted(false)
+{
+}
+
+PlatformScreen::~PlatformScreen()
+{
+ // do nothing
+}
+
+void
+PlatformScreen::updateKeyMap()
+{
+ getKeyState()->updateKeyMap();
+}
+
+void
+PlatformScreen::updateKeyState()
+{
+ getKeyState()->updateKeyState();
+ updateButtons();
+}
+
+void
+PlatformScreen::setHalfDuplexMask(KeyModifierMask mask)
+{
+ getKeyState()->setHalfDuplexMask(mask);
+}
+
+void
+PlatformScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button)
+{
+ getKeyState()->fakeKeyDown(id, mask, button);
+}
+
+bool
+PlatformScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button)
+{
+ return getKeyState()->fakeKeyRepeat(id, mask, count, button);
+}
+
+bool
+PlatformScreen::fakeKeyUp(KeyButton button)
+{
+ return getKeyState()->fakeKeyUp(button);
+}
+
+void
+PlatformScreen::fakeAllKeysUp()
+{
+ getKeyState()->fakeAllKeysUp();
+}
+
+bool
+PlatformScreen::fakeCtrlAltDel()
+{
+ return getKeyState()->fakeCtrlAltDel();
+}
+
+bool
+PlatformScreen::isKeyDown(KeyButton button) const
+{
+ return getKeyState()->isKeyDown(button);
+}
+
+KeyModifierMask
+PlatformScreen::getActiveModifiers() const
+{
+ return getKeyState()->getActiveModifiers();
+}
+
+KeyModifierMask
+PlatformScreen::pollActiveModifiers() const
+{
+ return getKeyState()->pollActiveModifiers();
+}
+
+SInt32
+PlatformScreen::pollActiveGroup() const
+{
+ return getKeyState()->pollActiveGroup();
+}
+
+void
+PlatformScreen::pollPressedKeys(KeyButtonSet& pressedKeys) const
+{
+ getKeyState()->pollPressedKeys(pressedKeys);
+}
+
+bool
+PlatformScreen::isDraggingStarted()
+{
+ if (App::instance().argsBase().m_enableDragDrop) {
+ return m_draggingStarted;
+ }
+ return false;
+}
diff --git a/src/lib/barrier/PlatformScreen.h b/src/lib/barrier/PlatformScreen.h
new file mode 100644
index 0000000..82cbfaa
--- /dev/null
+++ b/src/lib/barrier/PlatformScreen.h
@@ -0,0 +1,127 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/IPlatformScreen.h"
+#include "barrier/DragInformation.h"
+#include "common/stdexcept.h"
+
+//! Base screen implementation
+/*!
+This screen implementation is the superclass of all other screen
+implementations. It implements a handful of methods and requires
+subclasses to implement the rest.
+*/
+class PlatformScreen : public IPlatformScreen {
+public:
+ PlatformScreen(IEventQueue* events);
+ virtual ~PlatformScreen();
+
+ // IScreen overrides
+ virtual void* getEventTarget() const = 0;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const = 0;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
+
+ // IPrimaryScreen overrides
+ virtual void reconfigure(UInt32 activeSides) = 0;
+ virtual void warpCursor(SInt32 x, SInt32 y) = 0;
+ virtual UInt32 registerHotKey(KeyID key,
+ KeyModifierMask mask) = 0;
+ virtual void unregisterHotKey(UInt32 id) = 0;
+ virtual void fakeInputBegin() = 0;
+ virtual void fakeInputEnd() = 0;
+ virtual SInt32 getJumpZoneSize() const = 0;
+ virtual bool isAnyMouseButtonDown(UInt32& buttonID) const = 0;
+ virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0;
+
+ // ISecondaryScreen overrides
+ virtual void fakeMouseButton(ButtonID id, bool press) = 0;
+ virtual void fakeMouseMove(SInt32 x, SInt32 y) = 0;
+ virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const = 0;
+ virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const = 0;
+
+ // IKeyState overrides
+ virtual void updateKeyMap();
+ virtual void updateKeyState();
+ virtual void setHalfDuplexMask(KeyModifierMask);
+ virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button);
+ virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button);
+ virtual bool fakeKeyUp(KeyButton button);
+ virtual void fakeAllKeysUp();
+ virtual bool fakeCtrlAltDel();
+ virtual bool isKeyDown(KeyButton) const;
+ virtual KeyModifierMask
+ getActiveModifiers() const;
+ virtual KeyModifierMask
+ pollActiveModifiers() const;
+ virtual SInt32 pollActiveGroup() const;
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const;
+
+ virtual void setDraggingStarted(bool started) { m_draggingStarted = started; }
+ virtual bool isDraggingStarted();
+ virtual bool isFakeDraggingStarted() { return m_fakeDraggingStarted; }
+ virtual String& getDraggingFilename() { return m_draggingFilename; }
+ virtual void clearDraggingFilename() { }
+
+ // IPlatformScreen overrides
+ virtual void enable() = 0;
+ virtual void disable() = 0;
+ virtual void enter() = 0;
+ virtual bool leave() = 0;
+ virtual bool setClipboard(ClipboardID, const IClipboard*) = 0;
+ virtual void checkClipboards() = 0;
+ virtual void openScreensaver(bool notify) = 0;
+ virtual void closeScreensaver() = 0;
+ virtual void screensaver(bool activate) = 0;
+ virtual void resetOptions() = 0;
+ virtual void setOptions(const OptionsList& options) = 0;
+ virtual void setSequenceNumber(UInt32) = 0;
+ virtual bool isPrimary() const = 0;
+
+ virtual void fakeDraggingFiles(DragFileList fileList) { throw std::runtime_error("fakeDraggingFiles not implemented"); }
+ virtual const String&
+ getDropTarget() const { throw std::runtime_error("getDropTarget not implemented"); }
+
+protected:
+ //! Update mouse buttons
+ /*!
+ Subclasses must implement this method to update their internal mouse
+ button mapping and, if desired, state tracking.
+ */
+ virtual void updateButtons() = 0;
+
+ //! Get the key state
+ /*!
+ Subclasses must implement this method to return the platform specific
+ key state object that each subclass must have.
+ */
+ virtual IKeyState* getKeyState() const = 0;
+
+ // IPlatformScreen overrides
+ virtual void handleSystemEvent(const Event& event, void*) = 0;
+
+protected:
+ String m_draggingFilename;
+ bool m_draggingStarted;
+ bool m_fakeDraggingStarted;
+};
diff --git a/src/lib/barrier/PortableTaskBarReceiver.cpp b/src/lib/barrier/PortableTaskBarReceiver.cpp
new file mode 100644
index 0000000..384cacd
--- /dev/null
+++ b/src/lib/barrier/PortableTaskBarReceiver.cpp
@@ -0,0 +1,121 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/PortableTaskBarReceiver.h"
+#include "mt/Lock.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "arch/Arch.h"
+#include "common/Version.h"
+
+//
+// PortableTaskBarReceiver
+//
+
+PortableTaskBarReceiver::PortableTaskBarReceiver(IEventQueue* events) :
+ m_state(kNotRunning),
+ m_events(events)
+{
+ // do nothing
+}
+
+PortableTaskBarReceiver::~PortableTaskBarReceiver()
+{
+ // do nothing
+}
+
+void
+PortableTaskBarReceiver::updateStatus(INode* node, const String& errorMsg)
+{
+ {
+ // update our status
+ m_errorMessage = errorMsg;
+ if (node == NULL) {
+ if (m_errorMessage.empty()) {
+ m_state = kNotRunning;
+ }
+ else {
+ m_state = kNotWorking;
+ }
+ }
+ else {
+ m_state = kNotConnected;
+ }
+
+ // let subclasses have a go
+ onStatusChanged(node);
+ }
+
+ // tell task bar
+ ARCH->updateReceiver(this);
+}
+
+PortableTaskBarReceiver::EState
+PortableTaskBarReceiver::getStatus() const
+{
+ return m_state;
+}
+
+const String&
+PortableTaskBarReceiver::getErrorMessage() const
+{
+ return m_errorMessage;
+}
+
+void
+PortableTaskBarReceiver::quit()
+{
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+void
+PortableTaskBarReceiver::onStatusChanged(INode*)
+{
+ // do nothing
+}
+
+void
+PortableTaskBarReceiver::lock() const
+{
+ // do nothing
+}
+
+void
+PortableTaskBarReceiver::unlock() const
+{
+ // do nothing
+}
+
+std::string
+PortableTaskBarReceiver::getToolTip() const
+{
+ switch (m_state) {
+ case kNotRunning:
+ return barrier::string::sprintf("%s: Not running", kAppVersion);
+
+ case kNotWorking:
+ return barrier::string::sprintf("%s: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kNotConnected:
+ return barrier::string::sprintf("%s: Unknown", kAppVersion);
+
+ default:
+ return "";
+ }
+}
diff --git a/src/lib/barrier/PortableTaskBarReceiver.h b/src/lib/barrier/PortableTaskBarReceiver.h
new file mode 100644
index 0000000..d335e44
--- /dev/null
+++ b/src/lib/barrier/PortableTaskBarReceiver.h
@@ -0,0 +1,96 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/INode.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "base/log_outputters.h"
+#include "base/EventTypes.h"
+#include "base/Event.h"
+#include "base/String.h"
+#include "common/stdvector.h"
+
+class IEventQueue;
+
+//! Implementation of IArchTaskBarReceiver for the barrier server
+class PortableTaskBarReceiver : public IArchTaskBarReceiver {
+public:
+ PortableTaskBarReceiver(IEventQueue* events);
+ virtual ~PortableTaskBarReceiver();
+
+ //! @name manipulators
+ //@{
+
+ //! Update status
+ /*!
+ Determine the status and query required information from the server.
+ */
+ void updateStatus(INode*, const String& errorMsg);
+
+ //@}
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus() = 0;
+ virtual void runMenu(int x, int y) = 0;
+ virtual void primaryAction() = 0;
+ virtual void lock() const;
+ virtual void unlock() const;
+ virtual const Icon getIcon() const = 0;
+ virtual std::string getToolTip() const;
+
+protected:
+ typedef std::vector<String> Clients;
+ enum EState {
+ kNotRunning,
+ kNotWorking,
+ kNotConnected,
+ kConnected,
+ kMaxState
+ };
+
+ //! Get status
+ EState getStatus() const;
+
+ //! Get error message
+ const String& getErrorMessage() const;
+
+ //! Quit app
+ /*!
+ Causes the application to quit gracefully
+ */
+ void quit();
+
+ //! Status change notification
+ /*!
+ Called when status changes. The default implementation does
+ nothing.
+ */
+ virtual void onStatusChanged(INode* node);
+
+private:
+ EState m_state;
+ String m_errorMessage;
+
+ String m_server;
+ Clients m_clients;
+
+ IEventQueue* m_events;
+};
+
+IArchTaskBarReceiver* createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events);
diff --git a/src/lib/barrier/ProtocolUtil.cpp b/src/lib/barrier/ProtocolUtil.cpp
new file mode 100644
index 0000000..42fe69c
--- /dev/null
+++ b/src/lib/barrier/ProtocolUtil.cpp
@@ -0,0 +1,544 @@
+/*
+ * barrier -- 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 "barrier/ProtocolUtil.h"
+#include "io/IStream.h"
+#include "base/Log.h"
+#include "common/stdvector.h"
+
+#include <cctype>
+#include <cstring>
+
+//
+// ProtocolUtil
+//
+
+void
+ProtocolUtil::writef(barrier::IStream* stream, const char* fmt, ...)
+{
+ assert(stream != NULL);
+ assert(fmt != NULL);
+ LOG((CLOG_DEBUG2 "writef(%s)", fmt));
+
+ va_list args;
+ va_start(args, fmt);
+ UInt32 size = getLength(fmt, args);
+ va_end(args);
+ va_start(args, fmt);
+ vwritef(stream, fmt, size, args);
+ va_end(args);
+}
+
+bool
+ProtocolUtil::readf(barrier::IStream* stream, const char* fmt, ...)
+{
+ assert(stream != NULL);
+ assert(fmt != NULL);
+ LOG((CLOG_DEBUG2 "readf(%s)", fmt));
+
+ bool result;
+ va_list args;
+ va_start(args, fmt);
+ try {
+ vreadf(stream, fmt, args);
+ result = true;
+ }
+ catch (XIO&) {
+ result = false;
+ }
+ va_end(args);
+ return result;
+}
+
+void
+ProtocolUtil::vwritef(barrier::IStream* stream,
+ const char* fmt, UInt32 size, va_list args)
+{
+ assert(stream != NULL);
+ assert(fmt != NULL);
+
+ // done if nothing to write
+ if (size == 0) {
+ return;
+ }
+
+ // fill buffer
+ UInt8* buffer = new UInt8[size];
+ writef(buffer, fmt, args);
+
+ try {
+ // write buffer
+ stream->write(buffer, size);
+ LOG((CLOG_DEBUG2 "wrote %d bytes", size));
+
+ delete[] buffer;
+ }
+ catch (XBase&) {
+ delete[] buffer;
+ throw;
+ }
+}
+
+void
+ProtocolUtil::vreadf(barrier::IStream* stream, const char* fmt, va_list args)
+{
+ assert(stream != NULL);
+ assert(fmt != NULL);
+
+ // begin scanning
+ while (*fmt) {
+ if (*fmt == '%') {
+ // format specifier. determine argument size.
+ ++fmt;
+ UInt32 len = eatLength(&fmt);
+ switch (*fmt) {
+ case 'i': {
+ // check for valid length
+ assert(len == 1 || len == 2 || len == 4);
+
+ // read the data
+ UInt8 buffer[4];
+ read(stream, buffer, len);
+
+ // convert it
+ void* v = va_arg(args, void*);
+ switch (len) {
+ case 1:
+ // 1 byte integer
+ *static_cast<UInt8*>(v) = buffer[0];
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *static_cast<UInt8*>(v), *static_cast<UInt8*>(v)));
+ break;
+
+ case 2:
+ // 2 byte integer
+ *static_cast<UInt16*>(v) =
+ static_cast<UInt16>(
+ (static_cast<UInt16>(buffer[0]) << 8) |
+ static_cast<UInt16>(buffer[1]));
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *static_cast<UInt16*>(v), *static_cast<UInt16*>(v)));
+ break;
+
+ case 4:
+ // 4 byte integer
+ *static_cast<UInt32*>(v) =
+ (static_cast<UInt32>(buffer[0]) << 24) |
+ (static_cast<UInt32>(buffer[1]) << 16) |
+ (static_cast<UInt32>(buffer[2]) << 8) |
+ static_cast<UInt32>(buffer[3]);
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer: %d (0x%x)", len, *static_cast<UInt32*>(v), *static_cast<UInt32*>(v)));
+ break;
+ }
+ break;
+ }
+
+ case 'I': {
+ // check for valid length
+ assert(len == 1 || len == 2 || len == 4);
+
+ // read the vector length
+ UInt8 buffer[4];
+ read(stream, buffer, 4);
+ UInt32 n = (static_cast<UInt32>(buffer[0]) << 24) |
+ (static_cast<UInt32>(buffer[1]) << 16) |
+ (static_cast<UInt32>(buffer[2]) << 8) |
+ static_cast<UInt32>(buffer[3]);
+
+ // convert it
+ void* v = va_arg(args, void*);
+ switch (len) {
+ case 1:
+ // 1 byte integer
+ for (UInt32 i = 0; i < n; ++i) {
+ read(stream, buffer, 1);
+ static_cast<std::vector<UInt8>*>(v)->push_back(
+ buffer[0]);
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, static_cast<std::vector<UInt8>*>(v)->back(), static_cast<std::vector<UInt8>*>(v)->back()));
+ }
+ break;
+
+ case 2:
+ // 2 byte integer
+ for (UInt32 i = 0; i < n; ++i) {
+ read(stream, buffer, 2);
+ static_cast<std::vector<UInt16>*>(v)->push_back(
+ static_cast<UInt16>(
+ (static_cast<UInt16>(buffer[0]) << 8) |
+ static_cast<UInt16>(buffer[1])));
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, static_cast<std::vector<UInt16>*>(v)->back(), static_cast<std::vector<UInt16>*>(v)->back()));
+ }
+ break;
+
+ case 4:
+ // 4 byte integer
+ for (UInt32 i = 0; i < n; ++i) {
+ read(stream, buffer, 4);
+ static_cast<std::vector<UInt32>*>(v)->push_back(
+ (static_cast<UInt32>(buffer[0]) << 24) |
+ (static_cast<UInt32>(buffer[1]) << 16) |
+ (static_cast<UInt32>(buffer[2]) << 8) |
+ static_cast<UInt32>(buffer[3]));
+ LOG((CLOG_DEBUG2 "readf: read %d byte integer[%d]: %d (0x%x)", len, i, static_cast<std::vector<UInt32>*>(v)->back(), static_cast<std::vector<UInt32>*>(v)->back()));
+ }
+ break;
+ }
+ break;
+ }
+
+ case 's': {
+ assert(len == 0);
+
+ // read the string length
+ UInt8 buffer[128];
+ read(stream, buffer, 4);
+ UInt32 len = (static_cast<UInt32>(buffer[0]) << 24) |
+ (static_cast<UInt32>(buffer[1]) << 16) |
+ (static_cast<UInt32>(buffer[2]) << 8) |
+ static_cast<UInt32>(buffer[3]);
+
+ // use a fixed size buffer if its big enough
+ const bool useFixed = (len <= sizeof(buffer));
+
+ // allocate a buffer to read the data
+ UInt8* sBuffer = buffer;
+ if (!useFixed) {
+ sBuffer = new UInt8[len];
+ }
+
+ // read the data
+ try {
+ read(stream, sBuffer, len);
+ }
+ catch (...) {
+ if (!useFixed) {
+ delete[] sBuffer;
+ }
+ throw;
+ }
+
+ LOG((CLOG_DEBUG2 "readf: read %d byte string", len));
+
+ // save the data
+ String* dst = va_arg(args, String*);
+ dst->assign((const char*)sBuffer, len);
+
+ // release the buffer
+ if (!useFixed) {
+ delete[] sBuffer;
+ }
+ break;
+ }
+
+ case '%':
+ assert(len == 0);
+ break;
+
+ default:
+ assert(0 && "invalid format specifier");
+ }
+
+ // next format character
+ ++fmt;
+ }
+ else {
+ // read next character
+ char buffer[1];
+ read(stream, buffer, 1);
+
+ // verify match
+ if (buffer[0] != *fmt) {
+ LOG((CLOG_DEBUG2 "readf: format mismatch: %c vs %c", *fmt, buffer[0]));
+ throw XIOReadMismatch();
+ }
+
+ // next format character
+ ++fmt;
+ }
+ }
+}
+
+UInt32
+ProtocolUtil::getLength(const char* fmt, va_list args)
+{
+ UInt32 n = 0;
+ while (*fmt) {
+ if (*fmt == '%') {
+ // format specifier. determine argument size.
+ ++fmt;
+ UInt32 len = eatLength(&fmt);
+ switch (*fmt) {
+ case 'i':
+ assert(len == 1 || len == 2 || len == 4);
+ (void)va_arg(args, UInt32);
+ break;
+
+ case 'I':
+ assert(len == 1 || len == 2 || len == 4);
+ switch (len) {
+ case 1:
+ len = (UInt32)(va_arg(args, std::vector<UInt8>*))->size() + 4;
+ break;
+
+ case 2:
+ len = 2 * (UInt32)(va_arg(args, std::vector<UInt16>*))->size() + 4;
+ break;
+
+ case 4:
+ len = 4 * (UInt32)(va_arg(args, std::vector<UInt32>*))->size() + 4;
+ break;
+ }
+ break;
+
+ 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 '%':
+ assert(len == 0);
+ len = 1;
+ break;
+
+ default:
+ assert(0 && "invalid format specifier");
+ }
+
+ // accumulate size
+ n += len;
+ ++fmt;
+ }
+ else {
+ // regular character
+ ++n;
+ ++fmt;
+ }
+ }
+ return n;
+}
+
+void
+ProtocolUtil::writef(void* buffer, const char* fmt, va_list args)
+{
+ UInt8* dst = static_cast<UInt8*>(buffer);
+
+ while (*fmt) {
+ if (*fmt == '%') {
+ // format specifier. determine argument size.
+ ++fmt;
+ UInt32 len = eatLength(&fmt);
+ switch (*fmt) {
+ case 'i': {
+ const UInt32 v = va_arg(args, UInt32);
+ switch (len) {
+ case 1:
+ // 1 byte integer
+ *dst++ = static_cast<UInt8>(v & 0xff);
+ break;
+
+ case 2:
+ // 2 byte integer
+ *dst++ = static_cast<UInt8>((v >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( v & 0xff);
+ break;
+
+ case 4:
+ // 4 byte integer
+ *dst++ = static_cast<UInt8>((v >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((v >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((v >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( v & 0xff);
+ break;
+
+ default:
+ assert(0 && "invalid integer format length");
+ return;
+ }
+ break;
+ }
+
+ case 'I': {
+ switch (len) {
+ case 1: {
+ // 1 byte integers
+ const std::vector<UInt8>* list =
+ va_arg(args, const std::vector<UInt8>*);
+ const UInt32 n = (UInt32)list->size();
+ *dst++ = static_cast<UInt8>((n >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( n & 0xff);
+ for (UInt32 i = 0; i < n; ++i) {
+ *dst++ = (*list)[i];
+ }
+ break;
+ }
+
+ case 2: {
+ // 2 byte integers
+ const std::vector<UInt16>* list =
+ va_arg(args, const std::vector<UInt16>*);
+ const UInt32 n = (UInt32)list->size();
+ *dst++ = static_cast<UInt8>((n >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( n & 0xff);
+ for (UInt32 i = 0; i < n; ++i) {
+ const UInt16 v = (*list)[i];
+ *dst++ = static_cast<UInt8>((v >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( v & 0xff);
+ }
+ break;
+ }
+
+ case 4: {
+ // 4 byte integers
+ const std::vector<UInt32>* list =
+ va_arg(args, const std::vector<UInt32>*);
+ const UInt32 n = (UInt32)list->size();
+ *dst++ = static_cast<UInt8>((n >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((n >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( n & 0xff);
+ for (UInt32 i = 0; i < n; ++i) {
+ const UInt32 v = (*list)[i];
+ *dst++ = static_cast<UInt8>((v >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((v >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((v >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( v & 0xff);
+ }
+ break;
+ }
+
+ default:
+ assert(0 && "invalid integer vector format length");
+ return;
+ }
+ break;
+ }
+
+ case 's': {
+ assert(len == 0);
+ const String* src = va_arg(args, String*);
+ const UInt32 len = (src != NULL) ? (UInt32)src->size() : 0;
+ *dst++ = static_cast<UInt8>((len >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((len >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((len >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( len & 0xff);
+ if (len != 0) {
+ memcpy(dst, src->data(), len);
+ dst += len;
+ }
+ break;
+ }
+
+ case 'S': {
+ assert(len == 0);
+ const UInt32 len = va_arg(args, UInt32);
+ const UInt8* src = va_arg(args, UInt8*);
+ *dst++ = static_cast<UInt8>((len >> 24) & 0xff);
+ *dst++ = static_cast<UInt8>((len >> 16) & 0xff);
+ *dst++ = static_cast<UInt8>((len >> 8) & 0xff);
+ *dst++ = static_cast<UInt8>( len & 0xff);
+ memcpy(dst, src, len);
+ dst += len;
+ break;
+ }
+
+ case '%':
+ assert(len == 0);
+ *dst++ = '%';
+ break;
+
+ default:
+ assert(0 && "invalid format specifier");
+ }
+
+ // next format character
+ ++fmt;
+ }
+ else {
+ // copy regular character
+ *dst++ = *fmt++;
+ }
+ }
+}
+
+UInt32
+ProtocolUtil::eatLength(const char** pfmt)
+{
+ const char* fmt = *pfmt;
+ UInt32 n = 0;
+ for (;;) {
+ UInt32 d;
+ switch (*fmt) {
+ case '0': d = 0; break;
+ case '1': d = 1; break;
+ case '2': d = 2; break;
+ case '3': d = 3; break;
+ case '4': d = 4; break;
+ case '5': d = 5; break;
+ case '6': d = 6; break;
+ case '7': d = 7; break;
+ case '8': d = 8; break;
+ case '9': d = 9; break;
+ default: *pfmt = fmt; return n;
+ }
+ n = 10 * n + d;
+ ++fmt;
+ }
+}
+
+void
+ProtocolUtil::read(barrier::IStream* stream, void* vbuffer, UInt32 count)
+{
+ assert(stream != NULL);
+ assert(vbuffer != NULL);
+
+ UInt8* buffer = static_cast<UInt8*>(vbuffer);
+ while (count > 0) {
+ // read more
+ UInt32 n = stream->read(buffer, count);
+
+ // bail if stream has hungup
+ if (n == 0) {
+ LOG((CLOG_DEBUG2 "unexpected disconnect in readf(), %d bytes left", count));
+ throw XIOEndOfStream();
+ }
+
+ // prepare for next read
+ buffer += n;
+ count -= n;
+ }
+}
+
+
+//
+// XIOReadMismatch
+//
+
+String
+XIOReadMismatch::getWhat() const throw()
+{
+ return format("XIOReadMismatch", "ProtocolUtil::readf() mismatch");
+}
diff --git a/src/lib/barrier/ProtocolUtil.h b/src/lib/barrier/ProtocolUtil.h
new file mode 100644
index 0000000..78bb5ca
--- /dev/null
+++ b/src/lib/barrier/ProtocolUtil.h
@@ -0,0 +1,96 @@
+/*
+ * barrier -- 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 "io/XIO.h"
+#include "base/EventTypes.h"
+
+#include <stdarg.h>
+
+namespace barrier { class IStream; }
+
+//! Barrier protocol utilities
+/*!
+This class provides various functions for implementing the barrier
+protocol.
+*/
+class ProtocolUtil {
+public:
+ //! Write formatted data
+ /*!
+ Write formatted binary data to a stream. \c fmt consists of
+ regular characters and format specifiers. Format specifiers
+ begin with \%. All characters not part of a format specifier
+ are regular and are transmitted unchanged.
+
+ Format specifiers are:
+ - \%\% -- literal `\%'
+ - \%1i -- converts integer argument to 1 byte integer
+ - \%2i -- converts integer argument to 2 byte integer in NBO
+ - \%4i -- converts integer argument to 4 byte integer in NBO
+ - \%1I -- converts std::vector<UInt8>* to 1 byte integers
+ - \%2I -- converts std::vector<UInt16>* to 2 byte integers in NBO
+ - \%4I -- converts std::vector<UInt32>* to 4 byte integers in NBO
+ - \%s -- converts String* to stream of bytes
+ - \%S -- converts integer N and const UInt8* to stream of N bytes
+ */
+ static void writef(barrier::IStream*,
+ const char* fmt, ...);
+
+ //! Read formatted data
+ /*!
+ Read formatted binary data from a buffer. This performs the
+ reverse operation of writef(). Returns true if the entire
+ format was successfully parsed, false otherwise.
+
+ Format specifiers are:
+ - \%\% -- read (and discard) a literal `\%'
+ - \%1i -- reads a 1 byte integer; argument is a SInt32* or UInt32*
+ - \%2i -- reads an NBO 2 byte integer; arg is SInt32* or UInt32*
+ - \%4i -- reads an NBO 4 byte integer; arg is SInt32* or UInt32*
+ - \%1I -- reads 1 byte integers; arg is std::vector<UInt8>*
+ - \%2I -- reads NBO 2 byte integers; arg is std::vector<UInt16>*
+ - \%4I -- reads NBO 4 byte integers; arg is std::vector<UInt32>*
+ - \%s -- reads bytes; argument must be a String*, \b not a char*
+ */
+ static bool readf(barrier::IStream*,
+ const char* fmt, ...);
+
+private:
+ static void vwritef(barrier::IStream*,
+ const char* fmt, UInt32 size, va_list);
+ static void vreadf(barrier::IStream*,
+ const char* fmt, va_list);
+
+ static UInt32 getLength(const char* fmt, va_list);
+ static void writef(void*, const char* fmt, va_list);
+ static UInt32 eatLength(const char** fmt);
+ static void read(barrier::IStream*, void*, UInt32);
+};
+
+//! Mismatched read exception
+/*!
+Thrown by ProtocolUtil::readf() when the data being read does not
+match the format.
+*/
+class XIOReadMismatch : public XIO {
+public:
+ // XBase overrides
+ virtual String getWhat() const throw();
+};
diff --git a/src/lib/barrier/Screen.cpp b/src/lib/barrier/Screen.cpp
new file mode 100644
index 0000000..32442f6
--- /dev/null
+++ b/src/lib/barrier/Screen.cpp
@@ -0,0 +1,559 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/Screen.h"
+#include "barrier/IPlatformScreen.h"
+#include "barrier/protocol_types.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "server/ClientProxy.h"
+#include "base/TMethodEventJob.h"
+
+namespace barrier {
+
+//
+// Screen
+//
+
+Screen::Screen(IPlatformScreen* platformScreen, IEventQueue* events) :
+ m_screen(platformScreen),
+ m_isPrimary(platformScreen->isPrimary()),
+ m_enabled(false),
+ m_entered(m_isPrimary),
+ m_screenSaverSync(true),
+ m_fakeInput(false),
+ m_events(events),
+ m_mock(false),
+ m_enableDragDrop(false)
+{
+ assert(m_screen != NULL);
+
+ // reset options
+ resetOptions();
+
+ LOG((CLOG_DEBUG "opened display"));
+}
+
+Screen::~Screen()
+{
+ if (m_mock) {
+ return;
+ }
+
+ if (m_enabled) {
+ disable();
+ }
+ assert(!m_enabled);
+ assert(m_entered == m_isPrimary);
+ delete m_screen;
+ LOG((CLOG_DEBUG "closed display"));
+}
+
+void
+Screen::enable()
+{
+ assert(!m_enabled);
+
+ m_screen->updateKeyMap();
+ m_screen->updateKeyState();
+ m_screen->enable();
+ if (m_isPrimary) {
+ enablePrimary();
+ }
+ else {
+ enableSecondary();
+ }
+
+ // note activation
+ m_enabled = true;
+}
+
+void
+Screen::disable()
+{
+ assert(m_enabled);
+
+ if (!m_isPrimary && m_entered) {
+ leave();
+ }
+ else if (m_isPrimary && !m_entered) {
+ enter(0);
+ }
+ m_screen->disable();
+ if (m_isPrimary) {
+ disablePrimary();
+ }
+ else {
+ disableSecondary();
+ }
+
+ // note deactivation
+ m_enabled = false;
+}
+
+void
+Screen::enter(KeyModifierMask toggleMask)
+{
+ assert(m_entered == false);
+ LOG((CLOG_INFO "entering screen"));
+
+ // now on screen
+ m_entered = true;
+
+ m_screen->enter();
+ if (m_isPrimary) {
+ enterPrimary();
+ }
+ else {
+ enterSecondary(toggleMask);
+ }
+}
+
+bool
+Screen::leave()
+{
+ assert(m_entered == true);
+ LOG((CLOG_INFO "leaving screen"));
+
+ if (!m_screen->leave()) {
+ return false;
+ }
+ if (m_isPrimary) {
+ leavePrimary();
+ }
+ else {
+ leaveSecondary();
+ }
+
+ // make sure our idea of clipboard ownership is correct
+ m_screen->checkClipboards();
+
+ // now not on screen
+ m_entered = false;
+
+ return true;
+}
+
+void
+Screen::reconfigure(UInt32 activeSides)
+{
+ assert(m_isPrimary);
+ m_screen->reconfigure(activeSides);
+}
+
+void
+Screen::warpCursor(SInt32 x, SInt32 y)
+{
+ assert(m_isPrimary);
+ m_screen->warpCursor(x, y);
+}
+
+void
+Screen::setClipboard(ClipboardID id, const IClipboard* clipboard)
+{
+ m_screen->setClipboard(id, clipboard);
+}
+
+void
+Screen::grabClipboard(ClipboardID id)
+{
+ m_screen->setClipboard(id, NULL);
+}
+
+void
+Screen::screensaver(bool activate)
+{
+ if (!m_isPrimary) {
+ // activate/deactivation screen saver iff synchronization enabled
+ if (m_screenSaverSync) {
+ m_screen->screensaver(activate);
+ }
+ }
+}
+
+void
+Screen::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
+{
+ // check for ctrl+alt+del emulation
+ if (id == kKeyDelete &&
+ (mask & (KeyModifierControl | KeyModifierAlt)) ==
+ (KeyModifierControl | KeyModifierAlt)) {
+ LOG((CLOG_DEBUG "emulating ctrl+alt+del press"));
+ if (m_screen->fakeCtrlAltDel()) {
+ return;
+ }
+ }
+ m_screen->fakeKeyDown(id, mask, button);
+}
+
+void
+Screen::keyRepeat(KeyID id,
+ KeyModifierMask mask, SInt32 count, KeyButton button)
+{
+ assert(!m_isPrimary);
+ m_screen->fakeKeyRepeat(id, mask, count, button);
+}
+
+void
+Screen::keyUp(KeyID, KeyModifierMask, KeyButton button)
+{
+ m_screen->fakeKeyUp(button);
+}
+
+void
+Screen::mouseDown(ButtonID button)
+{
+ m_screen->fakeMouseButton(button, true);
+}
+
+void
+Screen::mouseUp(ButtonID button)
+{
+ m_screen->fakeMouseButton(button, false);
+}
+
+void
+Screen::mouseMove(SInt32 x, SInt32 y)
+{
+ assert(!m_isPrimary);
+ m_screen->fakeMouseMove(x, y);
+}
+
+void
+Screen::mouseRelativeMove(SInt32 dx, SInt32 dy)
+{
+ assert(!m_isPrimary);
+ m_screen->fakeMouseRelativeMove(dx, dy);
+}
+
+void
+Screen::mouseWheel(SInt32 xDelta, SInt32 yDelta)
+{
+ assert(!m_isPrimary);
+ m_screen->fakeMouseWheel(xDelta, yDelta);
+}
+
+void
+Screen::resetOptions()
+{
+ // reset options
+ m_halfDuplex = 0;
+
+ // if screen saver synchronization was off then turn it on since
+ // that's the default option state.
+ if (!m_screenSaverSync) {
+ m_screenSaverSync = true;
+ if (!m_isPrimary) {
+ m_screen->openScreensaver(false);
+ }
+ }
+
+ // let screen handle its own options
+ m_screen->resetOptions();
+}
+
+void
+Screen::setOptions(const OptionsList& options)
+{
+ // update options
+ bool oldScreenSaverSync = m_screenSaverSync;
+ for (UInt32 i = 0, n = (UInt32)options.size(); i < n; i += 2) {
+ if (options[i] == kOptionScreenSaverSync) {
+ m_screenSaverSync = (options[i + 1] != 0);
+ LOG((CLOG_DEBUG1 "screen saver synchronization %s", m_screenSaverSync ? "on" : "off"));
+ }
+ else if (options[i] == kOptionHalfDuplexCapsLock) {
+ if (options[i + 1] != 0) {
+ m_halfDuplex |= KeyModifierCapsLock;
+ }
+ else {
+ m_halfDuplex &= ~KeyModifierCapsLock;
+ }
+ LOG((CLOG_DEBUG1 "half-duplex caps-lock %s", ((m_halfDuplex & KeyModifierCapsLock) != 0) ? "on" : "off"));
+ }
+ else if (options[i] == kOptionHalfDuplexNumLock) {
+ if (options[i + 1] != 0) {
+ m_halfDuplex |= KeyModifierNumLock;
+ }
+ else {
+ m_halfDuplex &= ~KeyModifierNumLock;
+ }
+ LOG((CLOG_DEBUG1 "half-duplex num-lock %s", ((m_halfDuplex & KeyModifierNumLock) != 0) ? "on" : "off"));
+ }
+ else if (options[i] == kOptionHalfDuplexScrollLock) {
+ if (options[i + 1] != 0) {
+ m_halfDuplex |= KeyModifierScrollLock;
+ }
+ else {
+ m_halfDuplex &= ~KeyModifierScrollLock;
+ }
+ LOG((CLOG_DEBUG1 "half-duplex scroll-lock %s", ((m_halfDuplex & KeyModifierScrollLock) != 0) ? "on" : "off"));
+ }
+ }
+
+ // update half-duplex options
+ m_screen->setHalfDuplexMask(m_halfDuplex);
+
+ // update screen saver synchronization
+ if (!m_isPrimary && oldScreenSaverSync != m_screenSaverSync) {
+ if (m_screenSaverSync) {
+ m_screen->openScreensaver(false);
+ }
+ else {
+ m_screen->closeScreensaver();
+ }
+ }
+
+ // let screen handle its own options
+ m_screen->setOptions(options);
+}
+
+void
+Screen::setSequenceNumber(UInt32 seqNum)
+{
+ m_screen->setSequenceNumber(seqNum);
+}
+
+UInt32
+Screen::registerHotKey(KeyID key, KeyModifierMask mask)
+{
+ return m_screen->registerHotKey(key, mask);
+}
+
+void
+Screen::unregisterHotKey(UInt32 id)
+{
+ m_screen->unregisterHotKey(id);
+}
+
+void
+Screen::fakeInputBegin()
+{
+ assert(!m_fakeInput);
+
+ m_fakeInput = true;
+ m_screen->fakeInputBegin();
+}
+
+void
+Screen::fakeInputEnd()
+{
+ assert(m_fakeInput);
+
+ m_fakeInput = false;
+ m_screen->fakeInputEnd();
+}
+
+bool
+Screen::isOnScreen() const
+{
+ return m_entered;
+}
+
+bool
+Screen::isLockedToScreen() const
+{
+ // check for pressed mouse buttons
+ // HACK: commented out as it breaks new drag drop feature
+ UInt32 buttonID = 0;
+
+ if (m_screen->isAnyMouseButtonDown(buttonID)) {
+ if (buttonID != kButtonLeft) {
+ LOG((CLOG_DEBUG "locked by mouse buttonID: %d", buttonID));
+ }
+
+ if (m_enableDragDrop) {
+ return (buttonID == kButtonLeft) ? false : true;
+ }
+ else {
+ return true;
+ }
+ }
+
+ // not locked
+ return false;
+}
+
+SInt32
+Screen::getJumpZoneSize() const
+{
+ if (!m_isPrimary) {
+ return 0;
+ }
+ else {
+ return m_screen->getJumpZoneSize();
+ }
+}
+
+void
+Screen::getCursorCenter(SInt32& x, SInt32& y) const
+{
+ m_screen->getCursorCenter(x, y);
+}
+
+KeyModifierMask
+Screen::getActiveModifiers() const
+{
+ return m_screen->getActiveModifiers();
+}
+
+KeyModifierMask
+Screen::pollActiveModifiers() const
+{
+ return m_screen->pollActiveModifiers();
+}
+
+bool
+Screen::isDraggingStarted() const
+{
+ return m_screen->isDraggingStarted();
+}
+
+bool
+Screen::isFakeDraggingStarted() const
+{
+ return m_screen->isFakeDraggingStarted();
+}
+
+void
+Screen::setDraggingStarted(bool started)
+{
+ m_screen->setDraggingStarted(started);
+}
+
+void
+Screen::startDraggingFiles(DragFileList& fileList)
+{
+ m_screen->fakeDraggingFiles(fileList);
+}
+
+void
+Screen::setEnableDragDrop(bool enabled)
+{
+ m_enableDragDrop = enabled;
+}
+
+String&
+Screen::getDraggingFilename() const
+{
+ return m_screen->getDraggingFilename();
+}
+
+void
+Screen::clearDraggingFilename()
+{
+ m_screen->clearDraggingFilename();
+}
+
+const String&
+Screen::getDropTarget() const
+{
+ return m_screen->getDropTarget();
+}
+
+void*
+Screen::getEventTarget() const
+{
+ return m_screen;
+}
+
+bool
+Screen::getClipboard(ClipboardID id, IClipboard* clipboard) const
+{
+ return m_screen->getClipboard(id, clipboard);
+}
+
+void
+Screen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
+{
+ m_screen->getShape(x, y, w, h);
+}
+
+void
+Screen::getCursorPos(SInt32& x, SInt32& y) const
+{
+ m_screen->getCursorPos(x, y);
+}
+
+void
+Screen::enablePrimary()
+{
+ // get notified of screen saver activation/deactivation
+ m_screen->openScreensaver(true);
+
+ // claim screen changed size
+ m_events->addEvent(Event(m_events->forIScreen().shapeChanged(), getEventTarget()));
+}
+
+void
+Screen::enableSecondary()
+{
+ // assume primary has all clipboards
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ grabClipboard(id);
+ }
+
+ // disable the screen saver if synchronization is enabled
+ if (m_screenSaverSync) {
+ m_screen->openScreensaver(false);
+ }
+}
+
+void
+Screen::disablePrimary()
+{
+ // done with screen saver
+ m_screen->closeScreensaver();
+}
+
+void
+Screen::disableSecondary()
+{
+ // done with screen saver
+ m_screen->closeScreensaver();
+}
+
+void
+Screen::enterPrimary()
+{
+ // do nothing
+}
+
+void
+Screen::enterSecondary(KeyModifierMask)
+{
+ // do nothing
+}
+
+void
+Screen::leavePrimary()
+{
+ // we don't track keys while on the primary screen so update our
+ // idea of them now. this is particularly to update the state of
+ // the toggle modifiers.
+ m_screen->updateKeyState();
+}
+
+void
+Screen::leaveSecondary()
+{
+ // release any keys we think are still down
+ m_screen->fakeAllKeysUp();
+}
+
+}
diff --git a/src/lib/barrier/Screen.h b/src/lib/barrier/Screen.h
new file mode 100644
index 0000000..b16feff
--- /dev/null
+++ b/src/lib/barrier/Screen.h
@@ -0,0 +1,345 @@
+/*
+ * barrier -- 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 "barrier/DragInformation.h"
+#include "barrier/clipboard_types.h"
+#include "barrier/IScreen.h"
+#include "barrier/key_types.h"
+#include "barrier/mouse_types.h"
+#include "barrier/option_types.h"
+#include "base/String.h"
+
+class IClipboard;
+class IPlatformScreen;
+class IEventQueue;
+
+namespace barrier {
+
+//! Platform independent screen
+/*!
+This is a platform independent screen. It can work as either a
+primary or secondary screen.
+*/
+class Screen : public IScreen {
+public:
+ Screen(IPlatformScreen* platformScreen, IEventQueue* events);
+ virtual ~Screen();
+
+#ifdef TEST_ENV
+ Screen() : m_mock(true) { }
+#endif
+
+ //! @name manipulators
+ //@{
+
+ //! Activate screen
+ /*!
+ Activate the screen, preparing it to report system and user events.
+ For a secondary screen it also means disabling the screen saver if
+ synchronizing it and preparing to synthesize events.
+ */
+ virtual void enable();
+
+ //! Deactivate screen
+ /*!
+ Undoes the operations in activate() and events are no longer
+ reported. It also releases keys that are logically pressed.
+ */
+ virtual void disable();
+
+ //! Enter screen
+ /*!
+ Called when the user navigates to this screen. \p toggleMask has the
+ toggle keys that should be turned on on the secondary screen.
+ */
+ void enter(KeyModifierMask toggleMask);
+
+ //! Leave screen
+ /*!
+ Called when the user navigates off this screen.
+ */
+ bool leave();
+
+ //! Update configuration
+ /*!
+ This is called when the configuration has changed. \c activeSides
+ is a bitmask of EDirectionMask indicating which sides of the
+ primary screen are linked to clients.
+ */
+ void reconfigure(UInt32 activeSides);
+
+ //! Warp cursor
+ /*!
+ Warps the cursor to the absolute coordinates \c x,y. Also
+ discards input events up to and including the warp before
+ returning.
+ */
+ void warpCursor(SInt32 x, SInt32 y);
+
+ //! Set clipboard
+ /*!
+ Sets the system's clipboard contents. This is usually called
+ soon after an enter().
+ */
+ void setClipboard(ClipboardID, const IClipboard*);
+
+ //! Grab clipboard
+ /*!
+ Grabs (i.e. take ownership of) the system clipboard.
+ */
+ void grabClipboard(ClipboardID);
+
+ //! Activate/deactivate screen saver
+ /*!
+ Forcibly activates the screen saver if \c activate is true otherwise
+ forcibly deactivates it.
+ */
+ void screensaver(bool activate);
+
+ //! Notify of key press
+ /*!
+ Synthesize key events to generate a press of key \c id. If possible
+ match the given modifier mask. The KeyButton identifies the physical
+ key on the server that generated this key down. The client must
+ ensure that a key up or key repeat that uses the same KeyButton will
+ synthesize an up or repeat for the same client key synthesized by
+ keyDown().
+ */
+ void keyDown(KeyID id, KeyModifierMask, KeyButton);
+
+ //! Notify of key repeat
+ /*!
+ Synthesize key events to generate a press and release of key \c id
+ \c count times. If possible match the given modifier mask.
+ */
+ void keyRepeat(KeyID id, KeyModifierMask,
+ SInt32 count, KeyButton);
+
+ //! Notify of key release
+ /*!
+ Synthesize key events to generate a release of key \c id. If possible
+ match the given modifier mask.
+ */
+ void keyUp(KeyID id, KeyModifierMask, KeyButton);
+
+ //! Notify of mouse press
+ /*!
+ Synthesize mouse events to generate a press of mouse button \c id.
+ */
+ void mouseDown(ButtonID id);
+
+ //! Notify of mouse release
+ /*!
+ Synthesize mouse events to generate a release of mouse button \c id.
+ */
+ void mouseUp(ButtonID id);
+
+ //! Notify of mouse motion
+ /*!
+ Synthesize mouse events to generate mouse motion to the absolute
+ screen position \c xAbs,yAbs.
+ */
+ void mouseMove(SInt32 xAbs, SInt32 yAbs);
+
+ //! Notify of mouse motion
+ /*!
+ Synthesize mouse events to generate mouse motion by the relative
+ amount \c xRel,yRel.
+ */
+ void mouseRelativeMove(SInt32 xRel, SInt32 yRel);
+
+ //! Notify of mouse wheel motion
+ /*!
+ Synthesize mouse events to generate mouse wheel motion of \c xDelta
+ and \c yDelta. Deltas are positive for motion away from the user or
+ to the right and negative for motion towards the user or to the left.
+ Each wheel click should generate a delta of +/-120.
+ */
+ void mouseWheel(SInt32 xDelta, SInt32 yDelta);
+
+ //! Notify of options changes
+ /*!
+ Resets all options to their default values.
+ */
+ virtual void resetOptions();
+
+ //! Notify of options changes
+ /*!
+ Set options to given values. Ignores unknown options and doesn't
+ modify options that aren't given in \c options.
+ */
+ virtual void setOptions(const OptionsList& options);
+
+ //! Set clipboard sequence number
+ /*!
+ Sets the sequence number to use in subsequent clipboard events.
+ */
+ void setSequenceNumber(UInt32);
+
+ //! Register a system hotkey
+ /*!
+ Registers a system-wide hotkey for key \p key with modifiers \p mask.
+ Returns an id used to unregister the hotkey.
+ */
+ UInt32 registerHotKey(KeyID key, KeyModifierMask mask);
+
+ //! Unregister a system hotkey
+ /*!
+ Unregisters a previously registered hot key.
+ */
+ void unregisterHotKey(UInt32 id);
+
+ //! Prepare to synthesize input on primary screen
+ /*!
+ Prepares the primary screen to receive synthesized input. We do not
+ want to receive this synthesized input as user input so this method
+ ensures that we ignore it. Calls to \c fakeInputBegin() may not be
+ nested.
+ */
+ void fakeInputBegin();
+
+ //! Done synthesizing input on primary screen
+ /*!
+ Undoes whatever \c fakeInputBegin() did.
+ */
+ void fakeInputEnd();
+
+ //! Change dragging status
+ void setDraggingStarted(bool started);
+
+ //! Fake a files dragging operation
+ void startDraggingFiles(DragFileList& fileList);
+
+ void setEnableDragDrop(bool enabled);
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test if cursor on screen
+ /*!
+ Returns true iff the cursor is on the screen.
+ */
+ bool isOnScreen() const;
+
+ //! Get screen lock state
+ /*!
+ Returns true if there's any reason that the user should not be
+ allowed to leave the screen (usually because a button or key is
+ pressed). If this method returns true it logs a message as to
+ why at the CLOG_DEBUG level.
+ */
+ bool isLockedToScreen() const;
+
+ //! Get jump zone size
+ /*!
+ Return the jump zone size, the size of the regions on the edges of
+ the screen that cause the cursor to jump to another screen.
+ */
+ SInt32 getJumpZoneSize() const;
+
+ //! Get cursor center position
+ /*!
+ Return the cursor center position which is where we park the
+ cursor to compute cursor motion deltas and should be far from
+ the edges of the screen, typically the center.
+ */
+ void getCursorCenter(SInt32& x, SInt32& y) const;
+
+ //! Get the active modifiers
+ /*!
+ Returns the modifiers that are currently active according to our
+ shadowed state.
+ */
+ KeyModifierMask getActiveModifiers() const;
+
+ //! Get the active modifiers from OS
+ /*!
+ Returns the modifiers that are currently active according to the
+ operating system.
+ */
+ KeyModifierMask pollActiveModifiers() const;
+
+ //! Test if file is dragged on primary screen
+ bool isDraggingStarted() const;
+
+ //! Test if file is dragged on secondary screen
+ bool isFakeDraggingStarted() const;
+
+ //! Get the filename of the file being dragged
+ String& getDraggingFilename() const;
+
+ //! Clear the filename of the file that was dragged
+ void clearDraggingFilename();
+
+ //! Get the drop target directory
+ const String& getDropTarget() const;
+
+ //@}
+
+ // IScreen overrides
+ virtual void* getEventTarget() const;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const;
+
+ IPlatformScreen* getPlatformScreen() { return m_screen; }
+
+protected:
+ void enablePrimary();
+ void enableSecondary();
+ void disablePrimary();
+ void disableSecondary();
+
+ void enterPrimary();
+ void enterSecondary(KeyModifierMask toggleMask);
+ void leavePrimary();
+ void leaveSecondary();
+
+private:
+ // our platform dependent screen
+ IPlatformScreen* m_screen;
+
+ // true if screen is being used as a primary screen, false otherwise
+ bool m_isPrimary;
+
+ // true if screen is enabled
+ bool m_enabled;
+
+ // true if the cursor is on this screen
+ bool m_entered;
+
+ // true if screen saver should be synchronized to server
+ bool m_screenSaverSync;
+
+ // note toggle keys that toggles on up/down (false) or on
+ // transition (true)
+ KeyModifierMask m_halfDuplex;
+
+ // true if we're faking input on a primary screen
+ bool m_fakeInput;
+
+ IEventQueue* m_events;
+
+ bool m_mock;
+ bool m_enableDragDrop;
+};
+
+}
diff --git a/src/lib/barrier/ServerApp.cpp b/src/lib/barrier/ServerApp.cpp
new file mode 100644
index 0000000..112f290
--- /dev/null
+++ b/src/lib/barrier/ServerApp.cpp
@@ -0,0 +1,859 @@
+/*
+ * barrier -- 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 "barrier/ServerApp.h"
+
+#include "server/Server.h"
+#include "server/ClientListener.h"
+#include "server/ClientProxy.h"
+#include "server/PrimaryClient.h"
+#include "barrier/ArgParser.h"
+#include "barrier/Screen.h"
+#include "barrier/XScreen.h"
+#include "barrier/ServerTaskBarReceiver.h"
+#include "barrier/ServerArgs.h"
+#include "net/SocketMultiplexer.h"
+#include "net/TCPSocketFactory.h"
+#include "net/XSocket.h"
+#include "arch/Arch.h"
+#include "base/EventQueue.h"
+#include "base/log_outputters.h"
+#include "base/FunctionEventJob.h"
+#include "base/TMethodJob.h"
+#include "base/IEventQueue.h"
+#include "base/Log.h"
+#include "base/TMethodEventJob.h"
+#include "common/Version.h"
+
+#if SYSAPI_WIN32
+#include "arch/win32/ArchMiscWindows.h"
+#endif
+
+#if WINAPI_MSWINDOWS
+#include "platform/MSWindowsScreen.h"
+#elif WINAPI_XWINDOWS
+#include "platform/XWindowsScreen.h"
+#elif WINAPI_CARBON
+#include "platform/OSXScreen.h"
+#endif
+
+#if defined(__APPLE__)
+#include "platform/OSXDragSimulator.h"
+#endif
+
+#include <iostream>
+#include <stdio.h>
+#include <fstream>
+#include <sstream>
+
+//
+// ServerApp
+//
+
+ServerApp::ServerApp(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver) :
+ App(events, createTaskBarReceiver, new ServerArgs()),
+ m_server(NULL),
+ m_serverState(kUninitialized),
+ m_serverScreen(NULL),
+ m_primaryClient(NULL),
+ m_listener(NULL),
+ m_timer(NULL),
+ m_barrierAddress(NULL)
+{
+}
+
+ServerApp::~ServerApp()
+{
+}
+
+void
+ServerApp::parseArgs(int argc, const char* const* argv)
+{
+ ArgParser argParser(this);
+ bool result = argParser.parseServerArgs(args(), argc, argv);
+
+ if (!result || args().m_shouldExit) {
+ m_bye(kExitArgs);
+ }
+ else {
+ if (!args().m_barrierAddress.empty()) {
+ try {
+ *m_barrierAddress = NetworkAddress(args().m_barrierAddress,
+ kDefaultPort);
+ m_barrierAddress->resolve();
+ }
+ catch (XSocketAddress& e) {
+ LOG((CLOG_PRINT "%s: %s" BYE,
+ args().m_pname, e.what(), args().m_pname));
+ m_bye(kExitArgs);
+ }
+ }
+ }
+}
+
+void
+ServerApp::help()
+{
+ // window api args (windows/x-windows/carbon)
+#if WINAPI_XWINDOWS
+# define WINAPI_ARGS \
+ " [--display <display>] [--no-xinitthreads]"
+# define WINAPI_INFO \
+ " --display <display> connect to the X server at <display>\n" \
+ " --no-xinitthreads do not call XInitThreads()\n"
+#else
+# define WINAPI_ARGS ""
+# define WINAPI_INFO ""
+#endif
+
+ std::ostringstream buffer;
+ buffer << "Start the barrier server component." << std::endl
+ << std::endl
+ << "Usage: " << args().m_pname
+ << " [--address <address>]"
+ << " [--config <pathname>]"
+ << WINAPI_ARGS << HELP_SYS_ARGS << HELP_COMMON_ARGS << std::endl
+ << std::endl
+ << "Options:" << std::endl
+ << " -a, --address <address> listen for clients on the given address." << std::endl
+ << " -c, --config <pathname> use the named configuration file instead." << std::endl
+ << HELP_COMMON_INFO_1 << WINAPI_INFO << HELP_SYS_INFO << HELP_COMMON_INFO_2 << std::endl
+ << "Default options are marked with a *" << std::endl
+ << std::endl
+ << "The argument for --address is of the form: [<hostname>][:<port>]. The" << std::endl
+ << "hostname must be the address or hostname of an interface on the system." << std::endl
+ << "The default is to listen on all interfaces. The port overrides the" << std::endl
+ << "default port, " << kDefaultPort << "." << std::endl
+ << std::endl
+ << "If no configuration file pathname is provided then the first of the" << std::endl
+ << "following to load successfully sets the configuration:" << std::endl
+ << " $HOME/" << USR_CONFIG_NAME << std::endl
+ << " " << ARCH->concatPath(ARCH->getSystemDirectory(), SYS_CONFIG_NAME) << std::endl;
+
+ LOG((CLOG_PRINT "%s", buffer.str().c_str()));
+}
+
+void
+ServerApp::reloadSignalHandler(Arch::ESignal, void*)
+{
+ IEventQueue* events = App::instance().getEvents();
+ events->addEvent(Event(events->forServerApp().reloadConfig(),
+ events->getSystemTarget()));
+}
+
+void
+ServerApp::reloadConfig(const Event&, void*)
+{
+ LOG((CLOG_DEBUG "reload configuration"));
+ if (loadConfig(args().m_configFile)) {
+ if (m_server != NULL) {
+ m_server->setConfig(*args().m_config);
+ }
+ LOG((CLOG_NOTE "reloaded configuration"));
+ }
+}
+
+void
+ServerApp::loadConfig()
+{
+ bool loaded = false;
+
+ // load the config file, if specified
+ if (!args().m_configFile.empty()) {
+ loaded = loadConfig(args().m_configFile);
+ }
+
+ // load the default configuration if no explicit file given
+ else {
+ // get the user's home directory
+ String path = ARCH->getUserDirectory();
+ if (!path.empty()) {
+ // complete path
+ path = ARCH->concatPath(path, USR_CONFIG_NAME);
+
+ // now try loading the user's configuration
+ if (loadConfig(path)) {
+ loaded = true;
+ args().m_configFile = path;
+ }
+ }
+ if (!loaded) {
+ // try the system-wide config file
+ path = ARCH->getSystemDirectory();
+ if (!path.empty()) {
+ path = ARCH->concatPath(path, SYS_CONFIG_NAME);
+ if (loadConfig(path)) {
+ loaded = true;
+ args().m_configFile = path;
+ }
+ }
+ }
+ }
+
+ if (!loaded) {
+ LOG((CLOG_PRINT "%s: no configuration available", args().m_pname));
+ m_bye(kExitConfig);
+ }
+}
+
+bool
+ServerApp::loadConfig(const String& pathname)
+{
+ try {
+ // load configuration
+ LOG((CLOG_DEBUG "opening configuration \"%s\"", pathname.c_str()));
+ std::ifstream configStream(pathname.c_str());
+ if (!configStream.is_open()) {
+ // report failure to open configuration as a debug message
+ // since we try several paths and we expect some to be
+ // missing.
+ LOG((CLOG_DEBUG "cannot open configuration \"%s\"",
+ pathname.c_str()));
+ return false;
+ }
+ configStream >> *args().m_config;
+ LOG((CLOG_DEBUG "configuration read successfully"));
+ return true;
+ }
+ catch (XConfigRead& e) {
+ // report error in configuration file
+ LOG((CLOG_ERR "cannot read configuration \"%s\": %s",
+ pathname.c_str(), e.what()));
+ }
+ return false;
+}
+
+void
+ServerApp::forceReconnect(const Event&, void*)
+{
+ if (m_server != NULL) {
+ m_server->disconnect();
+ }
+}
+
+void
+ServerApp::handleClientConnected(const Event&, void* vlistener)
+{
+ ClientListener* listener = static_cast<ClientListener*>(vlistener);
+ ClientProxy* client = listener->getNextClient();
+ if (client != NULL) {
+ m_server->adoptClient(client);
+ updateStatus();
+ }
+}
+
+void
+ServerApp::handleClientsDisconnected(const Event&, void*)
+{
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+void
+ServerApp::closeServer(Server* server)
+{
+ if (server == NULL) {
+ return;
+ }
+
+ // tell all clients to disconnect
+ server->disconnect();
+
+ // wait for clients to disconnect for up to timeout seconds
+ double timeout = 3.0;
+ EventQueueTimer* timer = m_events->newOneShotTimer(timeout, NULL);
+ m_events->adoptHandler(Event::kTimer, timer,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::handleClientsDisconnected));
+ m_events->adoptHandler(m_events->forServer().disconnected(), server,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::handleClientsDisconnected));
+
+ m_events->loop();
+
+ m_events->removeHandler(Event::kTimer, timer);
+ m_events->deleteTimer(timer);
+ m_events->removeHandler(m_events->forServer().disconnected(), server);
+
+ // done with server
+ delete server;
+}
+
+void
+ServerApp::stopRetryTimer()
+{
+ if (m_timer != NULL) {
+ m_events->deleteTimer(m_timer);
+ m_events->removeHandler(Event::kTimer, NULL);
+ m_timer = NULL;
+ }
+}
+
+void
+ServerApp::updateStatus()
+{
+ updateStatus("");
+}
+
+void ServerApp::updateStatus(const String& msg)
+{
+ if (m_taskBarReceiver)
+ {
+ m_taskBarReceiver->updateStatus(m_server, msg);
+ }
+}
+
+void
+ServerApp::closeClientListener(ClientListener* listen)
+{
+ if (listen != NULL) {
+ m_events->removeHandler(m_events->forClientListener().connected(), listen);
+ delete listen;
+ }
+}
+
+void
+ServerApp::stopServer()
+{
+ if (m_serverState == kStarted) {
+ closeServer(m_server);
+ closeClientListener(m_listener);
+ m_server = NULL;
+ m_listener = NULL;
+ m_serverState = kInitialized;
+ }
+ else if (m_serverState == kStarting) {
+ stopRetryTimer();
+ m_serverState = kInitialized;
+ }
+ assert(m_server == NULL);
+ assert(m_listener == NULL);
+}
+
+void
+ServerApp::closePrimaryClient(PrimaryClient* primaryClient)
+{
+ delete primaryClient;
+}
+
+void
+ServerApp::closeServerScreen(barrier::Screen* screen)
+{
+ if (screen != NULL) {
+ m_events->removeHandler(m_events->forIScreen().error(),
+ screen->getEventTarget());
+ m_events->removeHandler(m_events->forIScreen().suspend(),
+ screen->getEventTarget());
+ m_events->removeHandler(m_events->forIScreen().resume(),
+ screen->getEventTarget());
+ delete screen;
+ }
+}
+
+void ServerApp::cleanupServer()
+{
+ stopServer();
+ if (m_serverState == kInitialized) {
+ closePrimaryClient(m_primaryClient);
+ closeServerScreen(m_serverScreen);
+ m_primaryClient = NULL;
+ m_serverScreen = NULL;
+ m_serverState = kUninitialized;
+ }
+ else if (m_serverState == kInitializing ||
+ m_serverState == kInitializingToStart) {
+ stopRetryTimer();
+ m_serverState = kUninitialized;
+ }
+ assert(m_primaryClient == NULL);
+ assert(m_serverScreen == NULL);
+ assert(m_serverState == kUninitialized);
+}
+
+void
+ServerApp::retryHandler(const Event&, void*)
+{
+ // discard old timer
+ assert(m_timer != NULL);
+ stopRetryTimer();
+
+ // try initializing/starting the server again
+ switch (m_serverState) {
+ case kUninitialized:
+ case kInitialized:
+ case kStarted:
+ assert(0 && "bad internal server state");
+ break;
+
+ case kInitializing:
+ LOG((CLOG_DEBUG1 "retry server initialization"));
+ m_serverState = kUninitialized;
+ if (!initServer()) {
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ break;
+
+ case kInitializingToStart:
+ LOG((CLOG_DEBUG1 "retry server initialization"));
+ m_serverState = kUninitialized;
+ if (!initServer()) {
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ else if (m_serverState == kInitialized) {
+ LOG((CLOG_DEBUG1 "starting server"));
+ if (!startServer()) {
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ }
+ break;
+
+ case kStarting:
+ LOG((CLOG_DEBUG1 "retry starting server"));
+ m_serverState = kInitialized;
+ if (!startServer()) {
+ m_events->addEvent(Event(Event::kQuit));
+ }
+ break;
+ }
+}
+
+bool ServerApp::initServer()
+{
+ // skip if already initialized or initializing
+ if (m_serverState != kUninitialized) {
+ return true;
+ }
+
+ double retryTime;
+ barrier::Screen* serverScreen = NULL;
+ PrimaryClient* primaryClient = NULL;
+ try {
+ String name = args().m_config->getCanonicalName(args().m_name);
+ serverScreen = openServerScreen();
+ primaryClient = openPrimaryClient(name, serverScreen);
+ m_serverScreen = serverScreen;
+ m_primaryClient = primaryClient;
+ m_serverState = kInitialized;
+ updateStatus();
+ return true;
+ }
+ catch (XScreenUnavailable& e) {
+ LOG((CLOG_WARN "primary screen unavailable: %s", e.what()));
+ closePrimaryClient(primaryClient);
+ closeServerScreen(serverScreen);
+ updateStatus(String("primary screen unavailable: ") + e.what());
+ retryTime = e.getRetryTime();
+ }
+ catch (XScreenOpenFailure& e) {
+ LOG((CLOG_CRIT "failed to start server: %s", e.what()));
+ closePrimaryClient(primaryClient);
+ closeServerScreen(serverScreen);
+ return false;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "failed to start server: %s", e.what()));
+ closePrimaryClient(primaryClient);
+ closeServerScreen(serverScreen);
+ return false;
+ }
+
+ if (args().m_restartable) {
+ // install a timer and handler to retry later
+ assert(m_timer == NULL);
+ LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
+ m_timer = m_events->newOneShotTimer(retryTime, NULL);
+ m_events->adoptHandler(Event::kTimer, m_timer,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::retryHandler));
+ m_serverState = kInitializing;
+ return true;
+ }
+ else {
+ // don't try again
+ return false;
+ }
+}
+
+barrier::Screen*
+ServerApp::openServerScreen()
+{
+ barrier::Screen* screen = createScreen();
+ screen->setEnableDragDrop(argsBase().m_enableDragDrop);
+ m_events->adoptHandler(m_events->forIScreen().error(),
+ screen->getEventTarget(),
+ new TMethodEventJob<ServerApp>(
+ this, &ServerApp::handleScreenError));
+ m_events->adoptHandler(m_events->forIScreen().suspend(),
+ screen->getEventTarget(),
+ new TMethodEventJob<ServerApp>(
+ this, &ServerApp::handleSuspend));
+ m_events->adoptHandler(m_events->forIScreen().resume(),
+ screen->getEventTarget(),
+ new TMethodEventJob<ServerApp>(
+ this, &ServerApp::handleResume));
+ return screen;
+}
+
+bool
+ServerApp::startServer()
+{
+ // skip if already started or starting
+ if (m_serverState == kStarting || m_serverState == kStarted) {
+ return true;
+ }
+
+ // initialize if necessary
+ if (m_serverState != kInitialized) {
+ if (!initServer()) {
+ // hard initialization failure
+ return false;
+ }
+ if (m_serverState == kInitializing) {
+ // not ready to start
+ m_serverState = kInitializingToStart;
+ return true;
+ }
+ assert(m_serverState == kInitialized);
+ }
+
+ double retryTime;
+ ClientListener* listener = NULL;
+ try {
+ listener = openClientListener(args().m_config->getBarrierAddress());
+ m_server = openServer(*args().m_config, m_primaryClient);
+ listener->setServer(m_server);
+ m_server->setListener(listener);
+ m_listener = listener;
+ updateStatus();
+ LOG((CLOG_NOTE "started server, waiting for clients"));
+ m_serverState = kStarted;
+ return true;
+ }
+ catch (XSocketAddressInUse& e) {
+ LOG((CLOG_WARN "cannot listen for clients: %s", e.what()));
+ closeClientListener(listener);
+ updateStatus(String("cannot listen for clients: ") + e.what());
+ retryTime = 10.0;
+ }
+ catch (XBase& e) {
+ LOG((CLOG_CRIT "failed to start server: %s", e.what()));
+ closeClientListener(listener);
+ return false;
+ }
+
+ if (args().m_restartable) {
+ // install a timer and handler to retry later
+ assert(m_timer == NULL);
+ LOG((CLOG_DEBUG "retry in %.0f seconds", retryTime));
+ m_timer = m_events->newOneShotTimer(retryTime, NULL);
+ m_events->adoptHandler(Event::kTimer, m_timer,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::retryHandler));
+ m_serverState = kStarting;
+ return true;
+ }
+ else {
+ // don't try again
+ return false;
+ }
+}
+
+barrier::Screen*
+ServerApp::createScreen()
+{
+#if WINAPI_MSWINDOWS
+ return new barrier::Screen(new MSWindowsScreen(
+ true, args().m_noHooks, args().m_stopOnDeskSwitch, m_events), m_events);
+#elif WINAPI_XWINDOWS
+ return new barrier::Screen(new XWindowsScreen(
+ args().m_display, true, args().m_disableXInitThreads, 0, m_events), m_events);
+#elif WINAPI_CARBON
+ return new barrier::Screen(new OSXScreen(m_events, true), m_events);
+#endif
+}
+
+PrimaryClient*
+ServerApp::openPrimaryClient(const String& name, barrier::Screen* screen)
+{
+ LOG((CLOG_DEBUG1 "creating primary screen"));
+ return new PrimaryClient(name, screen);
+
+}
+
+void
+ServerApp::handleScreenError(const Event&, void*)
+{
+ LOG((CLOG_CRIT "error on screen"));
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+void
+ServerApp::handleSuspend(const Event&, void*)
+{
+ if (!m_suspended) {
+ LOG((CLOG_INFO "suspend"));
+ stopServer();
+ m_suspended = true;
+ }
+}
+
+void
+ServerApp::handleResume(const Event&, void*)
+{
+ if (m_suspended) {
+ LOG((CLOG_INFO "resume"));
+ startServer();
+ m_suspended = false;
+ }
+}
+
+ClientListener*
+ServerApp::openClientListener(const NetworkAddress& address)
+{
+ ClientListener* listen = new ClientListener(
+ address,
+ new TCPSocketFactory(m_events, getSocketMultiplexer()),
+ m_events,
+ args().m_enableCrypto);
+
+ m_events->adoptHandler(
+ m_events->forClientListener().connected(), listen,
+ new TMethodEventJob<ServerApp>(
+ this, &ServerApp::handleClientConnected, listen));
+
+ return listen;
+}
+
+Server*
+ServerApp::openServer(Config& config, PrimaryClient* primaryClient)
+{
+ Server* server = new Server(config, primaryClient, m_serverScreen, m_events, args());
+ try {
+ m_events->adoptHandler(
+ m_events->forServer().disconnected(), server,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::handleNoClients));
+
+ m_events->adoptHandler(
+ m_events->forServer().screenSwitched(), server,
+ new TMethodEventJob<ServerApp>(this, &ServerApp::handleScreenSwitched));
+
+ } catch (std::bad_alloc &ba) {
+ delete server;
+ throw ba;
+ }
+
+ return server;
+}
+
+void
+ServerApp::handleNoClients(const Event&, void*)
+{
+ updateStatus();
+}
+
+void
+ServerApp::handleScreenSwitched(const Event& e, void*)
+{
+}
+
+int
+ServerApp::mainLoop()
+{
+ // create socket multiplexer. this must happen after daemonization
+ // on unix because threads evaporate across a fork().
+ SocketMultiplexer multiplexer;
+ setSocketMultiplexer(&multiplexer);
+
+ // if configuration has no screens then add this system
+ // as the default
+ if (args().m_config->begin() == args().m_config->end()) {
+ args().m_config->addScreen(args().m_name);
+ }
+
+ // set the contact address, if provided, in the config.
+ // otherwise, if the config doesn't have an address, use
+ // the default.
+ if (m_barrierAddress->isValid()) {
+ args().m_config->setBarrierAddress(*m_barrierAddress);
+ }
+ else if (!args().m_config->getBarrierAddress().isValid()) {
+ args().m_config->setBarrierAddress(NetworkAddress(kDefaultPort));
+ }
+
+ // canonicalize the primary screen name
+ String primaryName = args().m_config->getCanonicalName(args().m_name);
+ if (primaryName.empty()) {
+ LOG((CLOG_CRIT "unknown screen name `%s'", args().m_name.c_str()));
+ return kExitFailed;
+ }
+
+ // start server, etc
+ appUtil().startNode();
+
+ // init ipc client after node start, since create a new screen wipes out
+ // the event queue (the screen ctors call adoptBuffer).
+ if (argsBase().m_enableIpc) {
+ initIpcClient();
+ }
+
+ // handle hangup signal by reloading the server's configuration
+ ARCH->setSignalHandler(Arch::kHANGUP, &reloadSignalHandler, NULL);
+ m_events->adoptHandler(m_events->forServerApp().reloadConfig(),
+ m_events->getSystemTarget(),
+ new TMethodEventJob<ServerApp>(this, &ServerApp::reloadConfig));
+
+ // handle force reconnect event by disconnecting clients. they'll
+ // reconnect automatically.
+ m_events->adoptHandler(m_events->forServerApp().forceReconnect(),
+ m_events->getSystemTarget(),
+ new TMethodEventJob<ServerApp>(this, &ServerApp::forceReconnect));
+
+ // to work around the sticky meta keys problem, we'll give users
+ // the option to reset the state of barriers
+ m_events->adoptHandler(m_events->forServerApp().resetServer(),
+ m_events->getSystemTarget(),
+ new TMethodEventJob<ServerApp>(this, &ServerApp::resetServer));
+
+ // run event loop. if startServer() failed we're supposed to retry
+ // later. the timer installed by startServer() will take care of
+ // that.
+ DAEMON_RUNNING(true);
+
+#if defined(MAC_OS_X_VERSION_10_7)
+
+ Thread thread(
+ new TMethodJob<ServerApp>(
+ this, &ServerApp::runEventsLoop,
+ NULL));
+
+ // wait until carbon loop is ready
+ OSXScreen* screen = dynamic_cast<OSXScreen*>(
+ m_serverScreen->getPlatformScreen());
+ screen->waitForCarbonLoop();
+
+ runCocoaApp();
+#else
+ m_events->loop();
+#endif
+
+ DAEMON_RUNNING(false);
+
+ // close down
+ LOG((CLOG_DEBUG1 "stopping server"));
+ m_events->removeHandler(m_events->forServerApp().forceReconnect(),
+ m_events->getSystemTarget());
+ m_events->removeHandler(m_events->forServerApp().reloadConfig(),
+ m_events->getSystemTarget());
+ cleanupServer();
+ updateStatus();
+ LOG((CLOG_NOTE "stopped server"));
+
+ if (argsBase().m_enableIpc) {
+ cleanupIpcClient();
+ }
+
+ return kExitSuccess;
+}
+
+void ServerApp::resetServer(const Event&, void*)
+{
+ LOG((CLOG_DEBUG1 "resetting server"));
+ stopServer();
+ cleanupServer();
+ startServer();
+}
+
+int
+ServerApp::runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup)
+{
+ // general initialization
+ m_barrierAddress = new NetworkAddress;
+ args().m_config = new Config(m_events);
+ args().m_pname = ARCH->getBasename(argv[0]);
+
+ // install caller's output filter
+ if (outputter != NULL) {
+ CLOG->insert(outputter);
+ }
+
+ // run
+ int result = startup(argc, argv);
+
+ if (m_taskBarReceiver)
+ {
+ // done with task bar receiver
+ delete m_taskBarReceiver;
+ }
+
+ delete args().m_config;
+ delete m_barrierAddress;
+ return result;
+}
+
+int daemonMainLoopStatic(int argc, const char** argv) {
+ return ServerApp::instance().daemonMainLoop(argc, argv);
+}
+
+int
+ServerApp::standardStartup(int argc, char** argv)
+{
+ initApp(argc, argv);
+
+ // daemonize if requested
+ if (args().m_daemon) {
+ return ARCH->daemonize(daemonName(), daemonMainLoopStatic);
+ }
+ else {
+ return mainLoop();
+ }
+}
+
+int
+ServerApp::foregroundStartup(int argc, char** argv)
+{
+ initApp(argc, argv);
+
+ // never daemonize
+ return mainLoop();
+}
+
+const char*
+ServerApp::daemonName() const
+{
+#if SYSAPI_WIN32
+ return "Barrier Server";
+#elif SYSAPI_UNIX
+ return "barriers";
+#endif
+}
+
+const char*
+ServerApp::daemonInfo() const
+{
+#if SYSAPI_WIN32
+ return "Shares this computers mouse and keyboard with other computers.";
+#elif SYSAPI_UNIX
+ return "";
+#endif
+}
+
+void
+ServerApp::startNode()
+{
+ // start the server. if this return false then we've failed and
+ // we shouldn't retry.
+ LOG((CLOG_DEBUG1 "starting server"));
+ if (!startServer()) {
+ m_bye(kExitFailed);
+ }
+}
diff --git a/src/lib/barrier/ServerApp.h b/src/lib/barrier/ServerApp.h
new file mode 100644
index 0000000..528aa24
--- /dev/null
+++ b/src/lib/barrier/ServerApp.h
@@ -0,0 +1,127 @@
+/*
+ * barrier -- 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 "barrier/ArgsBase.h"
+#include "barrier/App.h"
+#include "base/String.h"
+#include "server/Config.h"
+#include "net/NetworkAddress.h"
+#include "arch/Arch.h"
+#include "arch/IArchMultithread.h"
+#include "barrier/ArgsBase.h"
+#include "base/EventTypes.h"
+
+#include <map>
+
+enum EServerState {
+ kUninitialized,
+ kInitializing,
+ kInitializingToStart,
+ kInitialized,
+ kStarting,
+ kStarted
+};
+
+class Server;
+namespace barrier { class Screen; }
+class ClientListener;
+class EventQueueTimer;
+class ILogOutputter;
+class IEventQueue;
+class ServerArgs;
+
+class ServerApp : public App {
+public:
+ ServerApp(IEventQueue* events, CreateTaskBarReceiverFunc createTaskBarReceiver);
+ virtual ~ServerApp();
+
+ // Parse server specific command line arguments.
+ void parseArgs(int argc, const char* const* argv);
+
+ // Prints help specific to server.
+ void help();
+
+ // Returns arguments that are common and for server.
+ ServerArgs& args() const { return (ServerArgs&)argsBase(); }
+
+ const char* daemonName() const;
+ const char* daemonInfo() const;
+
+ // TODO: Document these functions.
+ static void reloadSignalHandler(Arch::ESignal, void*);
+
+ void reloadConfig(const Event&, void*);
+ void loadConfig();
+ bool loadConfig(const String& pathname);
+ void forceReconnect(const Event&, void*);
+ void resetServer(const Event&, void*);
+ void handleClientConnected(const Event&, void* vlistener);
+ void handleClientsDisconnected(const Event&, void*);
+ void closeServer(Server* server);
+ void stopRetryTimer();
+ void updateStatus();
+ void updateStatus(const String& msg);
+ void closeClientListener(ClientListener* listen);
+ void stopServer();
+ void closePrimaryClient(PrimaryClient* primaryClient);
+ void closeServerScreen(barrier::Screen* screen);
+ void cleanupServer();
+ bool initServer();
+ void retryHandler(const Event&, void*);
+ barrier::Screen* openServerScreen();
+ barrier::Screen* createScreen();
+ PrimaryClient* openPrimaryClient(const String& name, barrier::Screen* screen);
+ void handleScreenError(const Event&, void*);
+ void handleSuspend(const Event&, void*);
+ void handleResume(const Event&, void*);
+ ClientListener* openClientListener(const NetworkAddress& address);
+ Server* openServer(Config& config, PrimaryClient* primaryClient);
+ void handleNoClients(const Event&, void*);
+ bool startServer();
+ int mainLoop();
+ int runInner(int argc, char** argv, ILogOutputter* outputter, StartupFunc startup);
+ int standardStartup(int argc, char** argv);
+ int foregroundStartup(int argc, char** argv);
+ void startNode();
+
+ static ServerApp& instance() { return (ServerApp&)App::instance(); }
+
+ Server* getServerPtr() { return m_server; }
+
+ Server* m_server;
+ EServerState m_serverState;
+ barrier::Screen* m_serverScreen;
+ PrimaryClient* m_primaryClient;
+ ClientListener* m_listener;
+ EventQueueTimer* m_timer;
+ NetworkAddress* m_barrierAddress;
+
+private:
+ void handleScreenSwitched(const Event&, void* data);
+};
+
+// configuration file name
+#if SYSAPI_WIN32
+#define USR_CONFIG_NAME "barrier.sgc"
+#define SYS_CONFIG_NAME "barrier.sgc"
+#elif SYSAPI_UNIX
+#define USR_CONFIG_NAME ".barrier.conf"
+#define SYS_CONFIG_NAME "barrier.conf"
+#endif
diff --git a/src/lib/barrier/ServerArgs.cpp b/src/lib/barrier/ServerArgs.cpp
new file mode 100644
index 0000000..49832f2
--- /dev/null
+++ b/src/lib/barrier/ServerArgs.cpp
@@ -0,0 +1,25 @@
+/*
+ * barrier -- 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 "barrier/ServerArgs.h"
+
+ServerArgs::ServerArgs() :
+ m_configFile(),
+ m_config(NULL)
+{
+}
+
diff --git a/src/lib/barrier/ServerArgs.h b/src/lib/barrier/ServerArgs.h
new file mode 100644
index 0000000..9c6e568
--- /dev/null
+++ b/src/lib/barrier/ServerArgs.h
@@ -0,0 +1,32 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include "barrier/ArgsBase.h"
+
+class NetworkAddress;
+class Config;
+
+class ServerArgs : public ArgsBase {
+public:
+ ServerArgs();
+
+public:
+ String m_configFile;
+ Config* m_config;
+};
diff --git a/src/lib/barrier/ServerTaskBarReceiver.cpp b/src/lib/barrier/ServerTaskBarReceiver.cpp
new file mode 100644
index 0000000..b427cd1
--- /dev/null
+++ b/src/lib/barrier/ServerTaskBarReceiver.cpp
@@ -0,0 +1,138 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/ServerTaskBarReceiver.h"
+#include "server/Server.h"
+#include "mt/Lock.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "arch/Arch.h"
+#include "common/Version.h"
+
+//
+// ServerTaskBarReceiver
+//
+
+ServerTaskBarReceiver::ServerTaskBarReceiver(IEventQueue* events) :
+ m_state(kNotRunning),
+ m_events(events)
+{
+ // do nothing
+}
+
+ServerTaskBarReceiver::~ServerTaskBarReceiver()
+{
+ // do nothing
+}
+
+void
+ServerTaskBarReceiver::updateStatus(Server* server, const String& errorMsg)
+{
+ {
+ // update our status
+ m_errorMessage = errorMsg;
+ if (server == NULL) {
+ if (m_errorMessage.empty()) {
+ m_state = kNotRunning;
+ }
+ else {
+ m_state = kNotWorking;
+ }
+ }
+ else {
+ m_clients.clear();
+ server->getClients(m_clients);
+ if (m_clients.size() <= 1) {
+ m_state = kNotConnected;
+ }
+ else {
+ m_state = kConnected;
+ }
+ }
+
+ // let subclasses have a go
+ onStatusChanged(server);
+ }
+
+ // tell task bar
+ ARCH->updateReceiver(this);
+}
+
+ServerTaskBarReceiver::EState
+ServerTaskBarReceiver::getStatus() const
+{
+ return m_state;
+}
+
+const String&
+ServerTaskBarReceiver::getErrorMessage() const
+{
+ return m_errorMessage;
+}
+
+const ServerTaskBarReceiver::Clients&
+ServerTaskBarReceiver::getClients() const
+{
+ return m_clients;
+}
+
+void
+ServerTaskBarReceiver::quit()
+{
+ m_events->addEvent(Event(Event::kQuit));
+}
+
+void
+ServerTaskBarReceiver::onStatusChanged(Server*)
+{
+ // do nothing
+}
+
+void
+ServerTaskBarReceiver::lock() const
+{
+ // do nothing
+}
+
+void
+ServerTaskBarReceiver::unlock() const
+{
+ // do nothing
+}
+
+std::string
+ServerTaskBarReceiver::getToolTip() const
+{
+ switch (m_state) {
+ case kNotRunning:
+ return barrier::string::sprintf("%s: Not running", kAppVersion);
+
+ case kNotWorking:
+ return barrier::string::sprintf("%s: %s",
+ kAppVersion, m_errorMessage.c_str());
+
+ case kNotConnected:
+ return barrier::string::sprintf("%s: Waiting for clients", kAppVersion);
+
+ case kConnected:
+ return barrier::string::sprintf("%s: Connected", kAppVersion);
+
+ default:
+ return "";
+ }
+}
diff --git a/src/lib/barrier/ServerTaskBarReceiver.h b/src/lib/barrier/ServerTaskBarReceiver.h
new file mode 100644
index 0000000..3cef9c0
--- /dev/null
+++ b/src/lib/barrier/ServerTaskBarReceiver.h
@@ -0,0 +1,98 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "server/Server.h"
+#include "barrier/ServerApp.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "base/EventTypes.h"
+#include "base/String.h"
+#include "base/Event.h"
+#include "common/stdvector.h"
+
+class IEventQueue;
+
+//! Implementation of IArchTaskBarReceiver for the barrier server
+class ServerTaskBarReceiver : public IArchTaskBarReceiver {
+public:
+ ServerTaskBarReceiver(IEventQueue* events);
+ virtual ~ServerTaskBarReceiver();
+
+ //! @name manipulators
+ //@{
+
+ //! Update status
+ /*!
+ Determine the status and query required information from the server.
+ */
+ void updateStatus(Server*, const String& errorMsg);
+
+ void updateStatus(INode* n, const String& errorMsg) { updateStatus((Server*)n, errorMsg); }
+
+ //@}
+
+ // IArchTaskBarReceiver overrides
+ virtual void showStatus() = 0;
+ virtual void runMenu(int x, int y) = 0;
+ virtual void primaryAction() = 0;
+ virtual void lock() const;
+ virtual void unlock() const;
+ virtual const Icon getIcon() const = 0;
+ virtual std::string getToolTip() const;
+
+protected:
+ typedef std::vector<String> Clients;
+ enum EState {
+ kNotRunning,
+ kNotWorking,
+ kNotConnected,
+ kConnected,
+ kMaxState
+ };
+
+ //! Get status
+ EState getStatus() const;
+
+ //! Get error message
+ const String& getErrorMessage() const;
+
+ //! Get connected clients
+ const Clients& getClients() const;
+
+ //! Quit app
+ /*!
+ Causes the application to quit gracefully
+ */
+ void quit();
+
+ //! Status change notification
+ /*!
+ Called when status changes. The default implementation does
+ nothing.
+ */
+ virtual void onStatusChanged(Server* server);
+
+private:
+ EState m_state;
+ String m_errorMessage;
+ Clients m_clients;
+ IEventQueue* m_events;
+};
+
+IArchTaskBarReceiver* createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events);
diff --git a/src/lib/barrier/StreamChunker.cpp b/src/lib/barrier/StreamChunker.cpp
new file mode 100644
index 0000000..8b8971c
--- /dev/null
+++ b/src/lib/barrier/StreamChunker.cpp
@@ -0,0 +1,166 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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 "barrier/StreamChunker.h"
+
+#include "mt/Lock.h"
+#include "mt/Mutex.h"
+#include "barrier/FileChunk.h"
+#include "barrier/ClipboardChunk.h"
+#include "barrier/protocol_types.h"
+#include "base/EventTypes.h"
+#include "base/Event.h"
+#include "base/IEventQueue.h"
+#include "base/EventTypes.h"
+#include "base/Log.h"
+#include "base/Stopwatch.h"
+#include "base/String.h"
+#include "common/stdexcept.h"
+
+#include <fstream>
+
+using namespace std;
+
+static const size_t g_chunkSize = 32 * 1024; //32kb
+
+bool StreamChunker::s_isChunkingFile = false;
+bool StreamChunker::s_interruptFile = false;
+Mutex* StreamChunker::s_interruptMutex = NULL;
+
+void
+StreamChunker::sendFile(
+ char* filename,
+ IEventQueue* events,
+ void* eventTarget)
+{
+ s_isChunkingFile = true;
+
+ std::fstream file(static_cast<char*>(filename), std::ios::in | std::ios::binary);
+
+ if (!file.is_open()) {
+ throw runtime_error("failed to open file");
+ }
+
+ // check file size
+ file.seekg (0, std::ios::end);
+ size_t size = (size_t)file.tellg();
+
+ // send first message (file size)
+ String fileSize = barrier::string::sizeTypeToString(size);
+ FileChunk* sizeMessage = FileChunk::start(fileSize);
+
+ events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, sizeMessage));
+
+ // send chunk messages with a fixed chunk size
+ size_t sentLength = 0;
+ size_t chunkSize = g_chunkSize;
+ file.seekg (0, std::ios::beg);
+
+ while (true) {
+ if (s_interruptFile) {
+ s_interruptFile = false;
+ LOG((CLOG_DEBUG "file transmission interrupted"));
+ break;
+ }
+
+ events->addEvent(Event(events->forFile().keepAlive(), eventTarget));
+
+ // make sure we don't read too much from the mock data.
+ if (sentLength + chunkSize > size) {
+ chunkSize = size - sentLength;
+ }
+
+ char* chunkData = new char[chunkSize];
+ file.read(chunkData, chunkSize);
+ UInt8* data = reinterpret_cast<UInt8*>(chunkData);
+ FileChunk* fileChunk = FileChunk::data(data, chunkSize);
+ delete[] chunkData;
+
+ events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, fileChunk));
+
+ sentLength += chunkSize;
+ file.seekg (sentLength, std::ios::beg);
+
+ if (sentLength == size) {
+ break;
+ }
+ }
+
+ // send last message
+ FileChunk* end = FileChunk::end();
+
+ events->addEvent(Event(events->forFile().fileChunkSending(), eventTarget, end));
+
+ file.close();
+
+ s_isChunkingFile = false;
+}
+
+void
+StreamChunker::sendClipboard(
+ String& data,
+ size_t size,
+ ClipboardID id,
+ UInt32 sequence,
+ IEventQueue* events,
+ void* eventTarget)
+{
+ // send first message (data size)
+ String dataSize = barrier::string::sizeTypeToString(size);
+ ClipboardChunk* sizeMessage = ClipboardChunk::start(id, sequence, dataSize);
+
+ events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, sizeMessage));
+
+ // send clipboard chunk with a fixed size
+ size_t sentLength = 0;
+ size_t chunkSize = g_chunkSize;
+
+ while (true) {
+ events->addEvent(Event(events->forFile().keepAlive(), eventTarget));
+
+ // make sure we don't read too much from the mock data.
+ if (sentLength + chunkSize > size) {
+ chunkSize = size - sentLength;
+ }
+
+ String chunk(data.substr(sentLength, chunkSize).c_str(), chunkSize);
+ ClipboardChunk* dataChunk = ClipboardChunk::data(id, sequence, chunk);
+
+ events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, dataChunk));
+
+ sentLength += chunkSize;
+ if (sentLength == size) {
+ break;
+ }
+ }
+
+ // send last message
+ ClipboardChunk* end = ClipboardChunk::end(id, sequence);
+
+ events->addEvent(Event(events->forClipboard().clipboardSending(), eventTarget, end));
+
+ LOG((CLOG_DEBUG "sent clipboard size=%d", sentLength));
+}
+
+void
+StreamChunker::interruptFile()
+{
+ if (s_isChunkingFile) {
+ s_interruptFile = true;
+ LOG((CLOG_INFO "previous dragged file has become invalid"));
+ }
+}
diff --git a/src/lib/barrier/StreamChunker.h b/src/lib/barrier/StreamChunker.h
new file mode 100644
index 0000000..ab57c7e
--- /dev/null
+++ b/src/lib/barrier/StreamChunker.h
@@ -0,0 +1,45 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#include "barrier/clipboard_types.h"
+#include "base/String.h"
+
+class IEventQueue;
+class Mutex;
+
+class StreamChunker {
+public:
+ static void sendFile(
+ char* filename,
+ IEventQueue* events,
+ void* eventTarget);
+ static void sendClipboard(
+ String& data,
+ size_t size,
+ ClipboardID id,
+ UInt32 sequence,
+ IEventQueue* events,
+ void* eventTarget);
+ static void interruptFile();
+
+private:
+ static bool s_isChunkingFile;
+ static bool s_interruptFile;
+ static Mutex* s_interruptMutex;
+};
diff --git a/src/lib/barrier/ToolApp.cpp b/src/lib/barrier/ToolApp.cpp
new file mode 100644
index 0000000..ae85e6d
--- /dev/null
+++ b/src/lib/barrier/ToolApp.cpp
@@ -0,0 +1,205 @@
+/*
+ * barrier -- 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 "barrier/ToolApp.h"
+
+#include "barrier/ArgParser.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+#include "base/String.h"
+
+#include <iostream>
+#include <sstream>
+
+#if SYSAPI_WIN32
+#include "platform/MSWindowsSession.h"
+#endif
+
+#define JSON_URL "https://symless.com/account/json/"
+
+enum {
+ kErrorOk,
+ kErrorArgs,
+ kErrorException,
+ kErrorUnknown
+};
+
+UInt32
+ToolApp::run(int argc, char** argv)
+{
+ if (argc <= 1) {
+ std::cerr << "no args" << std::endl;
+ return kErrorArgs;
+ }
+
+ try {
+ ArgParser argParser(this);
+ bool result = argParser.parseToolArgs(m_args, argc, argv);
+
+ if (!result) {
+ m_bye(kExitArgs);
+ }
+
+ if (m_args.m_printActiveDesktopName) {
+#if SYSAPI_WIN32
+ MSWindowsSession session;
+ String name = session.getActiveDesktopName();
+ if (name.empty()) {
+ LOG((CLOG_CRIT "failed to get active desktop name"));
+ return kExitFailed;
+ }
+ else {
+ String output = barrier::string::sprintf("activeDesktop:%s", name.c_str());
+ LOG((CLOG_INFO "%s", output.c_str()));
+ }
+#endif
+ }
+ else if (m_args.m_loginAuthenticate) {
+ loginAuth();
+ }
+ else if (m_args.m_getInstalledDir) {
+ std::cout << ARCH->getInstalledDirectory() << std::endl;
+ }
+ else if (m_args.m_getProfileDir) {
+ std::cout << ARCH->getProfileDirectory() << std::endl;
+ }
+ else if (m_args.m_getArch) {
+ std::cout << ARCH->getPlatformName() << std::endl;
+ }
+ else if (m_args.m_notifyUpdate) {
+ notifyUpdate();
+ }
+ else if (m_args.m_notifyActivation) {
+ notifyActivation();
+ }
+ else {
+ throw XBarrier("Nothing to do");
+ }
+ }
+ catch (std::exception& e) {
+ LOG((CLOG_CRIT "An error occurred: %s\n", e.what()));
+ return kExitFailed;
+ }
+ catch (...) {
+ LOG((CLOG_CRIT "An unknown error occurred.\n"));
+ return kExitFailed;
+ }
+
+#if WINAPI_XWINDOWS
+ // HACK: avoid sigsegv on linux
+ m_bye(kErrorOk);
+#endif
+
+ return kErrorOk;
+}
+
+void
+ToolApp::help()
+{
+}
+
+void
+ToolApp::loginAuth()
+{
+ String credentials;
+ std::cin >> credentials;
+
+ std::vector<String> parts = barrier::string::splitString(credentials, ':');
+ size_t count = parts.size();
+
+ if (count == 2 ) {
+ String email = parts[0];
+ String password = parts[1];
+
+ std::stringstream ss;
+ ss << JSON_URL << "auth/";
+ ss << "?email=" << ARCH->internet().urlEncode(email);
+ ss << "&password=" << password;
+
+ std::cout << ARCH->internet().get(ss.str()) << std::endl;
+ }
+ else {
+ throw XBarrier("Invalid credentials.");
+ }
+}
+
+void
+ToolApp::notifyUpdate()
+{
+ String data;
+ std::cin >> data;
+
+ std::vector<String> parts = barrier::string::splitString(data, ':');
+ size_t count = parts.size();
+
+ if (count == 3) {
+ std::stringstream ss;
+ ss << JSON_URL << "notify/update";
+ ss << "?from=" << parts[0];
+ ss << "&to=" << parts[1];
+
+ std::cout << ARCH->internet().get(ss.str()) << std::endl;
+ }
+ else {
+ throw XBarrier("Invalid update data.");
+ }
+}
+
+void
+ToolApp::notifyActivation()
+{
+ String info;
+ std::cin >> info;
+
+ std::vector<String> parts = barrier::string::splitString(info, ':');
+ size_t count = parts.size();
+
+ if (count == 3 || count == 4) {
+ String action = parts[0];
+ String identity = parts[1];
+ String macHash = parts[2];
+ String os;
+
+ if (count == 4) {
+ os = parts[3];
+ }
+ else {
+ os = ARCH->getOSName();
+ }
+
+ std::stringstream ss;
+ ss << JSON_URL << "notify/";
+ ss << "?action=" << action;
+ ss << "&identity=" << ARCH->internet().urlEncode(identity);
+ ss << "&mac=" << ARCH->internet().urlEncode(macHash);
+ ss << "&os=" << ARCH->internet().urlEncode(ARCH->getOSName());
+ ss << "&arch=" << ARCH->internet().urlEncode(ARCH->getPlatformName());
+
+ try {
+ std::cout << ARCH->internet().get(ss.str()) << std::endl;
+ }
+ catch (std::exception& e) {
+ LOG((CLOG_NOTE "An error occurred during notification: %s\n", e.what()));
+ }
+ catch (...) {
+ LOG((CLOG_NOTE "An unknown error occurred during notification.\n"));
+ }
+ }
+ else {
+ LOG((CLOG_NOTE "notification failed"));
+ }
+}
diff --git a/src/lib/barrier/ToolApp.h b/src/lib/barrier/ToolApp.h
new file mode 100644
index 0000000..5cb9a7c
--- /dev/null
+++ b/src/lib/barrier/ToolApp.h
@@ -0,0 +1,37 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include "barrier/App.h"
+#include "barrier/ToolArgs.h"
+#include "common/basic_types.h"
+
+class ToolApp : public MinimalApp
+{
+public:
+ UInt32 run(int argc, char** argv);
+ void help();
+
+private:
+ void loginAuth();
+ void notifyActivation();
+ void notifyUpdate();
+
+private:
+ ToolArgs m_args;
+};
diff --git a/src/lib/barrier/ToolArgs.cpp b/src/lib/barrier/ToolArgs.cpp
new file mode 100644
index 0000000..634a784
--- /dev/null
+++ b/src/lib/barrier/ToolArgs.cpp
@@ -0,0 +1,29 @@
+/*
+ * barrier -- 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 "barrier/ToolArgs.h"
+
+ToolArgs::ToolArgs() :
+ m_printActiveDesktopName(false),
+ m_loginAuthenticate(false),
+ m_getInstalledDir(false),
+ m_getProfileDir(false),
+ m_getArch(false),
+ m_notifyActivation(false),
+ m_notifyUpdate(false)
+{
+}
diff --git a/src/lib/barrier/ToolArgs.h b/src/lib/barrier/ToolArgs.h
new file mode 100644
index 0000000..36b4be3
--- /dev/null
+++ b/src/lib/barrier/ToolArgs.h
@@ -0,0 +1,34 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include "base/String.h"
+
+class ToolArgs {
+public:
+ ToolArgs();
+
+public:
+ bool m_printActiveDesktopName;
+ bool m_loginAuthenticate;
+ bool m_getInstalledDir;
+ bool m_getProfileDir;
+ bool m_getArch;
+ bool m_notifyActivation;
+ bool m_notifyUpdate;
+};
diff --git a/src/lib/barrier/XBarrier.cpp b/src/lib/barrier/XBarrier.cpp
new file mode 100644
index 0000000..49a015e
--- /dev/null
+++ b/src/lib/barrier/XBarrier.cpp
@@ -0,0 +1,133 @@
+/*
+ * barrier -- 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 "barrier/XBarrier.h"
+#include "base/String.h"
+
+//
+// XBadClient
+//
+
+String
+XBadClient::getWhat() const throw()
+{
+ return "XBadClient";
+}
+
+
+//
+// XIncompatibleClient
+//
+
+XIncompatibleClient::XIncompatibleClient(int major, int minor) :
+ m_major(major),
+ m_minor(minor)
+{
+ // do nothing
+}
+
+int
+XIncompatibleClient::getMajor() const throw()
+{
+ return m_major;
+}
+
+int
+XIncompatibleClient::getMinor() const throw()
+{
+ return m_minor;
+}
+
+String
+XIncompatibleClient::getWhat() const throw()
+{
+ return format("XIncompatibleClient", "incompatible client %{1}.%{2}",
+ barrier::string::sprintf("%d", m_major).c_str(),
+ barrier::string::sprintf("%d", m_minor).c_str());
+}
+
+
+//
+// XDuplicateClient
+//
+
+XDuplicateClient::XDuplicateClient(const String& name) :
+ m_name(name)
+{
+ // do nothing
+}
+
+const String&
+XDuplicateClient::getName() const throw()
+{
+ return m_name;
+}
+
+String
+XDuplicateClient::getWhat() const throw()
+{
+ return format("XDuplicateClient", "duplicate client %{1}", m_name.c_str());
+}
+
+
+//
+// XUnknownClient
+//
+
+XUnknownClient::XUnknownClient(const String& name) :
+ m_name(name)
+{
+ // do nothing
+}
+
+const String&
+XUnknownClient::getName() const throw()
+{
+ return m_name;
+}
+
+String
+XUnknownClient::getWhat() const throw()
+{
+ return format("XUnknownClient", "unknown client %{1}", m_name.c_str());
+}
+
+
+//
+// XExitApp
+//
+
+XExitApp::XExitApp(int code) :
+ m_code(code)
+{
+ // do nothing
+}
+
+int
+XExitApp::getCode() const throw()
+{
+ return m_code;
+}
+
+String
+XExitApp::getWhat() const throw()
+{
+ return format(
+ "XExitApp", "exiting with code %{1}",
+ barrier::string::sprintf("%d", m_code).c_str());
+}
diff --git a/src/lib/barrier/XBarrier.h b/src/lib/barrier/XBarrier.h
new file mode 100644
index 0000000..fdf5213
--- /dev/null
+++ b/src/lib/barrier/XBarrier.h
@@ -0,0 +1,135 @@
+/*
+ * barrier -- 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/XBase.h"
+
+//! Generic barrier exception
+XBASE_SUBCLASS(XBarrier, XBase);
+
+//! Subscription error
+/*!
+Thrown when there is a problem with the subscription.
+*/
+XBASE_SUBCLASS(XSubscription, XBarrier);
+
+//! Client error exception
+/*!
+Thrown when the client fails to follow the protocol.
+*/
+XBASE_SUBCLASS_WHAT(XBadClient, XBarrier);
+
+//! Incompatible client exception
+/*!
+Thrown when a client attempting to connect has an incompatible version.
+*/
+class XIncompatibleClient : public XBarrier {
+public:
+ XIncompatibleClient(int major, int minor);
+
+ //! @name accessors
+ //@{
+
+ //! Get client's major version number
+ int getMajor() const throw();
+ //! Get client's minor version number
+ int getMinor() const throw();
+
+ //@}
+
+protected:
+ virtual String getWhat() const throw();
+
+private:
+ int m_major;
+ int m_minor;
+};
+
+//! Client already connected exception
+/*!
+Thrown when a client attempting to connect is using the same name as
+a client that is already connected.
+*/
+class XDuplicateClient : public XBarrier {
+public:
+ XDuplicateClient(const String& name);
+ virtual ~XDuplicateClient() _NOEXCEPT { }
+
+ //! @name accessors
+ //@{
+
+ //! Get client's name
+ virtual const String&
+ getName() const throw();
+
+ //@}
+
+protected:
+ virtual String getWhat() const throw();
+
+private:
+ String m_name;
+};
+
+//! Client not in map exception
+/*!
+Thrown when a client attempting to connect is using a name that is
+unknown to the server.
+*/
+class XUnknownClient : public XBarrier {
+public:
+ XUnknownClient(const String& name);
+ virtual ~XUnknownClient() _NOEXCEPT { }
+
+ //! @name accessors
+ //@{
+
+ //! Get the client's name
+ virtual const String&
+ getName() const throw();
+
+ //@}
+
+protected:
+ virtual String getWhat() const throw();
+
+private:
+ String m_name;
+};
+
+//! Generic exit eception
+/*!
+Thrown when we want to abort, with the opportunity to clean up. This is a
+little bit of a hack, but it's a better way of exiting, than just calling
+exit(int).
+*/
+class XExitApp : public XBarrier {
+public:
+ XExitApp(int code);
+ virtual ~XExitApp() _NOEXCEPT { }
+
+ //! Get the exit code
+ int getCode() const throw();
+
+protected:
+ virtual String getWhat() const throw();
+
+private:
+ int m_code;
+};
diff --git a/src/lib/barrier/XScreen.cpp b/src/lib/barrier/XScreen.cpp
new file mode 100644
index 0000000..a202240
--- /dev/null
+++ b/src/lib/barrier/XScreen.cpp
@@ -0,0 +1,68 @@
+/*
+ * barrier -- 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 "barrier/XScreen.h"
+
+//
+// XScreenOpenFailure
+//
+
+String
+XScreenOpenFailure::getWhat() const throw()
+{
+ return format("XScreenOpenFailure", "unable to open screen");
+}
+
+
+//
+// XScreenXInputFailure
+//
+
+String
+XScreenXInputFailure::getWhat() const throw()
+{
+ return "";
+}
+
+
+//
+// XScreenUnavailable
+//
+
+XScreenUnavailable::XScreenUnavailable(double timeUntilRetry) :
+ m_timeUntilRetry(timeUntilRetry)
+{
+ // do nothing
+}
+
+XScreenUnavailable::~XScreenUnavailable() _NOEXCEPT
+{
+ // do nothing
+}
+
+double
+XScreenUnavailable::getRetryTime() const
+{
+ return m_timeUntilRetry;
+}
+
+String
+XScreenUnavailable::getWhat() const throw()
+{
+ return format("XScreenUnavailable", "unable to open screen");
+}
diff --git a/src/lib/barrier/XScreen.h b/src/lib/barrier/XScreen.h
new file mode 100644
index 0000000..0cb511e
--- /dev/null
+++ b/src/lib/barrier/XScreen.h
@@ -0,0 +1,68 @@
+/*
+ * barrier -- 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/XBase.h"
+
+//! Generic screen exception
+XBASE_SUBCLASS(XScreen, XBase);
+
+//! Cannot open screen exception
+/*!
+Thrown when a screen cannot be opened or initialized.
+*/
+XBASE_SUBCLASS_WHAT(XScreenOpenFailure, XScreen);
+
+//! XInput exception
+/*!
+Thrown when an XInput error occurs
+*/
+XBASE_SUBCLASS_WHAT(XScreenXInputFailure, XScreen);
+
+//! Screen unavailable exception
+/*!
+Thrown when a screen cannot be opened or initialized but retrying later
+may be successful.
+*/
+class XScreenUnavailable : public XScreenOpenFailure {
+public:
+ /*!
+ \c timeUntilRetry is the suggested time the caller should wait until
+ trying to open the screen again.
+ */
+ XScreenUnavailable(double timeUntilRetry);
+ virtual ~XScreenUnavailable() _NOEXCEPT;
+
+ //! @name manipulators
+ //@{
+
+ //! Get retry time
+ /*!
+ Returns the suggested time to wait until retrying to open the screen.
+ */
+ double getRetryTime() const;
+
+ //@}
+
+protected:
+ virtual String getWhat() const throw();
+
+private:
+ double m_timeUntilRetry;
+};
diff --git a/src/lib/barrier/clipboard_types.h b/src/lib/barrier/clipboard_types.h
new file mode 100644
index 0000000..54f2732
--- /dev/null
+++ b/src/lib/barrier/clipboard_types.h
@@ -0,0 +1,42 @@
+/*
+ * barrier -- 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 "common/basic_types.h"
+
+//! Clipboard ID
+/*!
+Type to hold a clipboard identifier.
+*/
+typedef UInt8 ClipboardID;
+
+//! @name Clipboard identifiers
+//@{
+// clipboard identifiers. kClipboardClipboard is what is normally
+// considered the clipboard (e.g. the cut/copy/paste menu items
+// affect it). kClipboardSelection is the selection on those
+// platforms that can treat the selection as a clipboard (e.g. X
+// windows). clipboard identifiers must be sequential starting
+// at zero.
+static const ClipboardID kClipboardClipboard = 0;
+static const ClipboardID kClipboardSelection = 1;
+
+// the number of clipboards (i.e. one greater than the last clipboard id)
+static const ClipboardID kClipboardEnd = 2;
+//@}
diff --git a/src/lib/barrier/key_types.cpp b/src/lib/barrier/key_types.cpp
new file mode 100644
index 0000000..902670d
--- /dev/null
+++ b/src/lib/barrier/key_types.cpp
@@ -0,0 +1,208 @@
+/*
+ * barrier -- 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 "barrier/key_types.h"
+
+const KeyNameMapEntry kKeyNameMap[] = {
+ { "AltGr", kKeyAltGr },
+ { "Alt_L", kKeyAlt_L },
+ { "Alt_R", kKeyAlt_R },
+ { "AppMail", kKeyAppMail },
+ { "AppMedia", kKeyAppMedia },
+ { "AppUser1", kKeyAppUser1 },
+ { "AppUser2", kKeyAppUser2 },
+ { "AudioDown", kKeyAudioDown },
+ { "AudioMute", kKeyAudioMute },
+ { "AudioNext", kKeyAudioNext },
+ { "AudioPlay", kKeyAudioPlay },
+ { "AudioPrev", kKeyAudioPrev },
+ { "AudioStop", kKeyAudioStop },
+ { "AudioUp", kKeyAudioUp },
+ { "BackSpace", kKeyBackSpace },
+ { "Begin", kKeyBegin },
+ { "Break", kKeyBreak },
+ { "Cancel", kKeyCancel },
+ { "CapsLock", kKeyCapsLock },
+ { "Clear", kKeyClear },
+ { "Control_L", kKeyControl_L },
+ { "Control_R", kKeyControl_R },
+ { "Delete", kKeyDelete },
+ { "Down", kKeyDown },
+ { "Eject", kKeyEject },
+ { "End", kKeyEnd },
+ { "Escape", kKeyEscape },
+ { "Execute", kKeyExecute },
+ { "F1", kKeyF1 },
+ { "F2", kKeyF2 },
+ { "F3", kKeyF3 },
+ { "F4", kKeyF4 },
+ { "F5", kKeyF5 },
+ { "F6", kKeyF6 },
+ { "F7", kKeyF7 },
+ { "F8", kKeyF8 },
+ { "F9", kKeyF9 },
+ { "F10", kKeyF10 },
+ { "F11", kKeyF11 },
+ { "F12", kKeyF12 },
+ { "F13", kKeyF13 },
+ { "F14", kKeyF14 },
+ { "F15", kKeyF15 },
+ { "F16", kKeyF16 },
+ { "F17", kKeyF17 },
+ { "F18", kKeyF18 },
+ { "F19", kKeyF19 },
+ { "F20", kKeyF20 },
+ { "F21", kKeyF21 },
+ { "F22", kKeyF22 },
+ { "F23", kKeyF23 },
+ { "F24", kKeyF24 },
+ { "F25", kKeyF25 },
+ { "F26", kKeyF26 },
+ { "F27", kKeyF27 },
+ { "F28", kKeyF28 },
+ { "F29", kKeyF29 },
+ { "F30", kKeyF30 },
+ { "F31", kKeyF31 },
+ { "F32", kKeyF32 },
+ { "F33", kKeyF33 },
+ { "F34", kKeyF34 },
+ { "F35", kKeyF35 },
+ { "Find", kKeyFind },
+ { "Help", kKeyHelp },
+ { "Henkan", kKeyHenkan },
+ { "Home", kKeyHome },
+ { "Hyper_L", kKeyHyper_L },
+ { "Hyper_R", kKeyHyper_R },
+ { "Insert", kKeyInsert },
+ { "KP_0", kKeyKP_0 },
+ { "KP_1", kKeyKP_1 },
+ { "KP_2", kKeyKP_2 },
+ { "KP_3", kKeyKP_3 },
+ { "KP_4", kKeyKP_4 },
+ { "KP_5", kKeyKP_5 },
+ { "KP_6", kKeyKP_6 },
+ { "KP_7", kKeyKP_7 },
+ { "KP_8", kKeyKP_8 },
+ { "KP_9", kKeyKP_9 },
+ { "KP_Add", kKeyKP_Add },
+ { "KP_Begin", kKeyKP_Begin },
+ { "KP_Decimal", kKeyKP_Decimal },
+ { "KP_Delete", kKeyKP_Delete },
+ { "KP_Divide", kKeyKP_Divide },
+ { "KP_Down", kKeyKP_Down },
+ { "KP_End", kKeyKP_End },
+ { "KP_Enter", kKeyKP_Enter },
+ { "KP_Equal", kKeyKP_Equal },
+ { "KP_F1", kKeyKP_F1 },
+ { "KP_F2", kKeyKP_F2 },
+ { "KP_F3", kKeyKP_F3 },
+ { "KP_F4", kKeyKP_F4 },
+ { "KP_Home", kKeyKP_Home },
+ { "KP_Insert", kKeyKP_Insert },
+ { "KP_Left", kKeyKP_Left },
+ { "KP_Multiply", kKeyKP_Multiply },
+ { "KP_PageDown", kKeyKP_PageDown },
+ { "KP_PageUp", kKeyKP_PageUp },
+ { "KP_Right", kKeyKP_Right },
+ { "KP_Separator", kKeyKP_Separator },
+ { "KP_Space", kKeyKP_Space },
+ { "KP_Subtract", kKeyKP_Subtract },
+ { "KP_Tab", kKeyKP_Tab },
+ { "KP_Up", kKeyKP_Up },
+ { "Left", kKeyLeft },
+ { "LeftTab", kKeyLeftTab },
+ { "Linefeed", kKeyLinefeed },
+ { "Menu", kKeyMenu },
+ { "Meta_L", kKeyMeta_L },
+ { "Meta_R", kKeyMeta_R },
+ { "NumLock", kKeyNumLock },
+ { "PageDown", kKeyPageDown },
+ { "PageUp", kKeyPageUp },
+ { "Pause", kKeyPause },
+ { "Print", kKeyPrint },
+ { "Redo", kKeyRedo },
+ { "Return", kKeyReturn },
+ { "Right", kKeyRight },
+ { "ScrollLock", kKeyScrollLock },
+ { "Select", kKeySelect },
+ { "ShiftLock", kKeyShiftLock },
+ { "Shift_L", kKeyShift_L },
+ { "Shift_R", kKeyShift_R },
+ { "Sleep", kKeySleep },
+ { "Super_L", kKeySuper_L },
+ { "Super_R", kKeySuper_R },
+ { "SysReq", kKeySysReq },
+ { "Tab", kKeyTab },
+ { "Undo", kKeyUndo },
+ { "Up", kKeyUp },
+ { "WWWBack", kKeyWWWBack },
+ { "WWWFavorites", kKeyWWWFavorites },
+ { "WWWForward", kKeyWWWForward },
+ { "WWWHome", kKeyWWWHome },
+ { "WWWRefresh", kKeyWWWRefresh },
+ { "WWWSearch", kKeyWWWSearch },
+ { "WWWStop", kKeyWWWStop },
+ { "Zenkaku", kKeyZenkaku },
+ { "Space", 0x0020 },
+ { "Exclaim", 0x0021 },
+ { "DoubleQuote", 0x0022 },
+ { "Number", 0x0023 },
+ { "Dollar", 0x0024 },
+ { "Percent", 0x0025 },
+ { "Ampersand", 0x0026 },
+ { "Apostrophe", 0x0027 },
+ { "ParenthesisL", 0x0028 },
+ { "ParenthesisR", 0x0029 },
+ { "Asterisk", 0x002a },
+ { "Plus", 0x002b },
+ { "Comma", 0x002c },
+ { "Minus", 0x002d },
+ { "Period", 0x002e },
+ { "Slash", 0x002f },
+ { "Colon", 0x003a },
+ { "Semicolon", 0x003b },
+ { "Less", 0x003c },
+ { "Equal", 0x003d },
+ { "Greater", 0x003e },
+ { "Question", 0x003f },
+ { "At", 0x0040 },
+ { "BracketL", 0x005b },
+ { "Backslash", 0x005c },
+ { "BracketR", 0x005d },
+ { "Circumflex", 0x005e },
+ { "Underscore", 0x005f },
+ { "Grave", 0x0060 },
+ { "BraceL", 0x007b },
+ { "Bar", 0x007c },
+ { "BraceR", 0x007d },
+ { "Tilde", 0x007e },
+ { NULL, 0 },
+};
+
+const KeyModifierNameMapEntry kModifierNameMap[] = {
+ { "Alt", KeyModifierAlt },
+ { "AltGr", KeyModifierAltGr },
+// { "CapsLock", KeyModifierCapsLock },
+ { "Control", KeyModifierControl },
+ { "Meta", KeyModifierMeta },
+// { "NumLock", KeyModifierNumLock },
+// { "ScrollLock", KeyModifierScrollLock },
+ { "Shift", KeyModifierShift },
+ { "Super", KeyModifierSuper },
+ { NULL, 0 },
+};
diff --git a/src/lib/barrier/key_types.h b/src/lib/barrier/key_types.h
new file mode 100644
index 0000000..7a8ea53
--- /dev/null
+++ b/src/lib/barrier/key_types.h
@@ -0,0 +1,314 @@
+/*
+ * barrier -- 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 "common/basic_types.h"
+
+//! Key ID
+/*!
+Type to hold a key symbol identifier. The encoding is UTF-32, using
+U+E000 through U+EFFF for the various control keys (e.g. arrow
+keys, function keys, modifier keys, etc).
+*/
+typedef UInt32 KeyID;
+
+//! Key Code
+/*!
+Type to hold a physical key identifier. That is, it identifies a
+physical key on the keyboard. KeyButton 0 is reserved to be an
+invalid key; platforms that use 0 as a physical key identifier
+will have to remap that value to some arbitrary unused id.
+*/
+typedef UInt16 KeyButton;
+
+//! Modifier key mask
+/*!
+Type to hold a bitmask of key modifiers (e.g. shift keys).
+*/
+typedef UInt32 KeyModifierMask;
+
+//! Modifier key ID
+/*!
+Type to hold the id of a key modifier (e.g. a shift key).
+*/
+typedef UInt32 KeyModifierID;
+
+//! @name Modifier key masks
+//@{
+static const KeyModifierMask KeyModifierShift = 0x0001;
+static const KeyModifierMask KeyModifierControl = 0x0002;
+static const KeyModifierMask KeyModifierAlt = 0x0004;
+static const KeyModifierMask KeyModifierMeta = 0x0008;
+static const KeyModifierMask KeyModifierSuper = 0x0010;
+static const KeyModifierMask KeyModifierAltGr = 0x0020;
+static const KeyModifierMask KeyModifierLevel5Lock = 0x0040;
+static const KeyModifierMask KeyModifierCapsLock = 0x1000;
+static const KeyModifierMask KeyModifierNumLock = 0x2000;
+static const KeyModifierMask KeyModifierScrollLock = 0x4000;
+//@}
+
+//! @name Modifier key bits
+//@{
+static const UInt32 kKeyModifierBitNone = 16;
+static const UInt32 kKeyModifierBitShift = 0;
+static const UInt32 kKeyModifierBitControl = 1;
+static const UInt32 kKeyModifierBitAlt = 2;
+static const UInt32 kKeyModifierBitMeta = 3;
+static const UInt32 kKeyModifierBitSuper = 4;
+static const UInt32 kKeyModifierBitAltGr = 5;
+static const UInt32 kKeyModifierBitLevel5Lock = 6;
+static const UInt32 kKeyModifierBitCapsLock = 12;
+static const UInt32 kKeyModifierBitNumLock = 13;
+static const UInt32 kKeyModifierBitScrollLock = 14;
+static const SInt32 kKeyModifierNumBits = 16;
+//@}
+
+//! @name Modifier key identifiers
+//@{
+static const KeyModifierID kKeyModifierIDNull = 0;
+static const KeyModifierID kKeyModifierIDShift = 1;
+static const KeyModifierID kKeyModifierIDControl = 2;
+static const KeyModifierID kKeyModifierIDAlt = 3;
+static const KeyModifierID kKeyModifierIDMeta = 4;
+static const KeyModifierID kKeyModifierIDSuper = 5;
+static const KeyModifierID kKeyModifierIDAltGr = 6;
+static const KeyModifierID kKeyModifierIDLast = 7;
+//@}
+
+//! @name Key identifiers
+//@{
+// all identifiers except kKeyNone and those in 0xE000 to 0xE0FF
+// inclusive are equal to the corresponding X11 keysym - 0x1000.
+
+// no key
+static const KeyID kKeyNone = 0x0000;
+
+// TTY functions
+static const KeyID kKeyBackSpace = 0xEF08; /* back space, back char */
+static const KeyID kKeyTab = 0xEF09;
+static const KeyID kKeyLinefeed = 0xEF0A; /* Linefeed, LF */
+static const KeyID kKeyClear = 0xEF0B;
+static const KeyID kKeyReturn = 0xEF0D; /* Return, enter */
+static const KeyID kKeyPause = 0xEF13; /* Pause, hold */
+static const KeyID kKeyScrollLock = 0xEF14;
+static const KeyID kKeySysReq = 0xEF15;
+static const KeyID kKeyEscape = 0xEF1B;
+static const KeyID kKeyHenkan = 0xEF23; /* Start/Stop Conversion */
+static const KeyID kKeyKana = 0xEF26; /* Kana */
+static const KeyID kKeyHiraganaKatakana = 0xEF27; /* Hiragana/Katakana toggle */
+static const KeyID kKeyZenkaku = 0xEF2A; /* Zenkaku/Hankaku */
+static const KeyID kKeyKanzi = 0xEF2A; /* Kanzi */
+static const KeyID kKeyHangul = 0xEF31; /* Hangul */
+static const KeyID kKeyHanja = 0xEF34; /* Hanja */
+static const KeyID kKeyDelete = 0xEFFF; /* Delete, rubout */
+
+// cursor control
+static const KeyID kKeyHome = 0xEF50;
+static const KeyID kKeyLeft = 0xEF51; /* Move left, left arrow */
+static const KeyID kKeyUp = 0xEF52; /* Move up, up arrow */
+static const KeyID kKeyRight = 0xEF53; /* Move right, right arrow */
+static const KeyID kKeyDown = 0xEF54; /* Move down, down arrow */
+static const KeyID kKeyPageUp = 0xEF55;
+static const KeyID kKeyPageDown = 0xEF56;
+static const KeyID kKeyEnd = 0xEF57; /* EOL */
+static const KeyID kKeyBegin = 0xEF58; /* BOL */
+
+// misc functions
+static const KeyID kKeySelect = 0xEF60; /* Select, mark */
+static const KeyID kKeyPrint = 0xEF61;
+static const KeyID kKeyExecute = 0xEF62; /* Execute, run, do */
+static const KeyID kKeyInsert = 0xEF63; /* Insert, insert here */
+static const KeyID kKeyUndo = 0xEF65; /* Undo, oops */
+static const KeyID kKeyRedo = 0xEF66; /* redo, again */
+static const KeyID kKeyMenu = 0xEF67;
+static const KeyID kKeyFind = 0xEF68; /* Find, search */
+static const KeyID kKeyCancel = 0xEF69; /* Cancel, stop, abort, exit */
+static const KeyID kKeyHelp = 0xEF6A; /* Help */
+static const KeyID kKeyBreak = 0xEF6B;
+static const KeyID kKeyAltGr = 0xEF7E; /* Character set switch */
+static const KeyID kKeyNumLock = 0xEF7F;
+
+// keypad
+static const KeyID kKeyKP_Space = 0xEF80; /* space */
+static const KeyID kKeyKP_Tab = 0xEF89;
+static const KeyID kKeyKP_Enter = 0xEF8D; /* enter */
+static const KeyID kKeyKP_F1 = 0xEF91; /* PF1, KP_A, ... */
+static const KeyID kKeyKP_F2 = 0xEF92;
+static const KeyID kKeyKP_F3 = 0xEF93;
+static const KeyID kKeyKP_F4 = 0xEF94;
+static const KeyID kKeyKP_Home = 0xEF95;
+static const KeyID kKeyKP_Left = 0xEF96;
+static const KeyID kKeyKP_Up = 0xEF97;
+static const KeyID kKeyKP_Right = 0xEF98;
+static const KeyID kKeyKP_Down = 0xEF99;
+static const KeyID kKeyKP_PageUp = 0xEF9A;
+static const KeyID kKeyKP_PageDown = 0xEF9B;
+static const KeyID kKeyKP_End = 0xEF9C;
+static const KeyID kKeyKP_Begin = 0xEF9D;
+static const KeyID kKeyKP_Insert = 0xEF9E;
+static const KeyID kKeyKP_Delete = 0xEF9F;
+static const KeyID kKeyKP_Equal = 0xEFBD; /* equals */
+static const KeyID kKeyKP_Multiply = 0xEFAA;
+static const KeyID kKeyKP_Add = 0xEFAB;
+static const KeyID kKeyKP_Separator= 0xEFAC; /* separator, often comma */
+static const KeyID kKeyKP_Subtract = 0xEFAD;
+static const KeyID kKeyKP_Decimal = 0xEFAE;
+static const KeyID kKeyKP_Divide = 0xEFAF;
+static const KeyID kKeyKP_0 = 0xEFB0;
+static const KeyID kKeyKP_1 = 0xEFB1;
+static const KeyID kKeyKP_2 = 0xEFB2;
+static const KeyID kKeyKP_3 = 0xEFB3;
+static const KeyID kKeyKP_4 = 0xEFB4;
+static const KeyID kKeyKP_5 = 0xEFB5;
+static const KeyID kKeyKP_6 = 0xEFB6;
+static const KeyID kKeyKP_7 = 0xEFB7;
+static const KeyID kKeyKP_8 = 0xEFB8;
+static const KeyID kKeyKP_9 = 0xEFB9;
+
+// function keys
+static const KeyID kKeyF1 = 0xEFBE;
+static const KeyID kKeyF2 = 0xEFBF;
+static const KeyID kKeyF3 = 0xEFC0;
+static const KeyID kKeyF4 = 0xEFC1;
+static const KeyID kKeyF5 = 0xEFC2;
+static const KeyID kKeyF6 = 0xEFC3;
+static const KeyID kKeyF7 = 0xEFC4;
+static const KeyID kKeyF8 = 0xEFC5;
+static const KeyID kKeyF9 = 0xEFC6;
+static const KeyID kKeyF10 = 0xEFC7;
+static const KeyID kKeyF11 = 0xEFC8;
+static const KeyID kKeyF12 = 0xEFC9;
+static const KeyID kKeyF13 = 0xEFCA;
+static const KeyID kKeyF14 = 0xEFCB;
+static const KeyID kKeyF15 = 0xEFCC;
+static const KeyID kKeyF16 = 0xEFCD;
+static const KeyID kKeyF17 = 0xEFCE;
+static const KeyID kKeyF18 = 0xEFCF;
+static const KeyID kKeyF19 = 0xEFD0;
+static const KeyID kKeyF20 = 0xEFD1;
+static const KeyID kKeyF21 = 0xEFD2;
+static const KeyID kKeyF22 = 0xEFD3;
+static const KeyID kKeyF23 = 0xEFD4;
+static const KeyID kKeyF24 = 0xEFD5;
+static const KeyID kKeyF25 = 0xEFD6;
+static const KeyID kKeyF26 = 0xEFD7;
+static const KeyID kKeyF27 = 0xEFD8;
+static const KeyID kKeyF28 = 0xEFD9;
+static const KeyID kKeyF29 = 0xEFDA;
+static const KeyID kKeyF30 = 0xEFDB;
+static const KeyID kKeyF31 = 0xEFDC;
+static const KeyID kKeyF32 = 0xEFDD;
+static const KeyID kKeyF33 = 0xEFDE;
+static const KeyID kKeyF34 = 0xEFDF;
+static const KeyID kKeyF35 = 0xEFE0;
+
+// modifiers
+static const KeyID kKeyShift_L = 0xEFE1; /* Left shift */
+static const KeyID kKeyShift_R = 0xEFE2; /* Right shift */
+static const KeyID kKeyControl_L = 0xEFE3; /* Left control */
+static const KeyID kKeyControl_R = 0xEFE4; /* Right control */
+static const KeyID kKeyCapsLock = 0xEFE5; /* Caps lock */
+static const KeyID kKeyShiftLock = 0xEFE6; /* Shift lock */
+static const KeyID kKeyMeta_L = 0xEFE7; /* Left meta */
+static const KeyID kKeyMeta_R = 0xEFE8; /* Right meta */
+static const KeyID kKeyAlt_L = 0xEFE9; /* Left alt */
+static const KeyID kKeyAlt_R = 0xEFEA; /* Right alt */
+static const KeyID kKeySuper_L = 0xEFEB; /* Left super */
+static const KeyID kKeySuper_R = 0xEFEC; /* Right super */
+static const KeyID kKeyHyper_L = 0xEFED; /* Left hyper */
+static const KeyID kKeyHyper_R = 0xEFEE; /* Right hyper */
+
+// multi-key character composition
+static const KeyID kKeyCompose = 0xEF20;
+static const KeyID kKeyDeadGrave = 0x0300;
+static const KeyID kKeyDeadAcute = 0x0301;
+static const KeyID kKeyDeadCircumflex = 0x0302;
+static const KeyID kKeyDeadTilde = 0x0303;
+static const KeyID kKeyDeadMacron = 0x0304;
+static const KeyID kKeyDeadBreve = 0x0306;
+static const KeyID kKeyDeadAbovedot = 0x0307;
+static const KeyID kKeyDeadDiaeresis = 0x0308;
+static const KeyID kKeyDeadAbovering = 0x030a;
+static const KeyID kKeyDeadDoubleacute = 0x030b;
+static const KeyID kKeyDeadCaron = 0x030c;
+static const KeyID kKeyDeadCedilla = 0x0327;
+static const KeyID kKeyDeadOgonek = 0x0328;
+
+// more function and modifier keys
+static const KeyID kKeyLeftTab = 0xEE20;
+
+// update modifiers
+static const KeyID kKeySetModifiers = 0xEE06;
+static const KeyID kKeyClearModifiers = 0xEE07;
+
+// group change
+static const KeyID kKeyNextGroup = 0xEE08;
+static const KeyID kKeyPrevGroup = 0xEE0A;
+
+// extended keys
+static const KeyID kKeyEject = 0xE001;
+static const KeyID kKeySleep = 0xE05F;
+static const KeyID kKeyWWWBack = 0xE0A6;
+static const KeyID kKeyWWWForward = 0xE0A7;
+static const KeyID kKeyWWWRefresh = 0xE0A8;
+static const KeyID kKeyWWWStop = 0xE0A9;
+static const KeyID kKeyWWWSearch = 0xE0AA;
+static const KeyID kKeyWWWFavorites = 0xE0AB;
+static const KeyID kKeyWWWHome = 0xE0AC;
+static const KeyID kKeyAudioMute = 0xE0AD;
+static const KeyID kKeyAudioDown = 0xE0AE;
+static const KeyID kKeyAudioUp = 0xE0AF;
+static const KeyID kKeyAudioNext = 0xE0B0;
+static const KeyID kKeyAudioPrev = 0xE0B1;
+static const KeyID kKeyAudioStop = 0xE0B2;
+static const KeyID kKeyAudioPlay = 0xE0B3;
+static const KeyID kKeyAppMail = 0xE0B4;
+static const KeyID kKeyAppMedia = 0xE0B5;
+static const KeyID kKeyAppUser1 = 0xE0B6;
+static const KeyID kKeyAppUser2 = 0xE0B7;
+static const KeyID kKeyBrightnessDown = 0xE0B8;
+static const KeyID kKeyBrightnessUp = 0xE0B9;
+static const KeyID kKeyMissionControl = 0xE0C0;
+static const KeyID kKeyLaunchpad = 0xE0C1;
+
+//@}
+
+struct KeyNameMapEntry {
+ const char* m_name;
+ KeyID m_id;
+};
+struct KeyModifierNameMapEntry {
+ const char* m_name;
+ KeyModifierMask m_mask;
+};
+
+//! Key name to KeyID table
+/*!
+A table of key names to the corresponding KeyID. Only the keys listed
+above plus non-alphanumeric ASCII characters are in the table. The end
+of the table is the first pair with a NULL m_name.
+*/
+extern const struct KeyNameMapEntry kKeyNameMap[];
+
+//! Modifier key name to KeyModifierMask table
+/*!
+A table of modifier key names to the corresponding KeyModifierMask.
+The end of the table is the first pair with a NULL m_name.
+*/
+extern const struct KeyModifierNameMapEntry kModifierNameMap[];
diff --git a/src/lib/barrier/mouse_types.h b/src/lib/barrier/mouse_types.h
new file mode 100644
index 0000000..cf860c0
--- /dev/null
+++ b/src/lib/barrier/mouse_types.h
@@ -0,0 +1,41 @@
+/*
+ * barrier -- 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/EventTypes.h"
+
+//! Mouse button ID
+/*!
+Type to hold a mouse button identifier.
+*/
+typedef UInt8 ButtonID;
+
+//! @name Mouse button identifiers
+//@{
+static const ButtonID kButtonNone = 0;
+static const ButtonID kButtonLeft = 1;
+static const ButtonID kButtonMiddle = 2;
+static const ButtonID kButtonRight = 3;
+static const ButtonID kButtonExtra0 = 4;
+
+static const ButtonID kMacButtonRight = 2;
+static const ButtonID kMacButtonMiddle = 3;
+//@}
+
+static const UInt8 NumButtonIDs = 5;
diff --git a/src/lib/barrier/option_types.h b/src/lib/barrier/option_types.h
new file mode 100644
index 0000000..6323e37
--- /dev/null
+++ b/src/lib/barrier/option_types.h
@@ -0,0 +1,99 @@
+/*
+ * barrier -- 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/EventTypes.h"
+#include "common/stdvector.h"
+
+//! Option ID
+/*!
+Type to hold an option identifier.
+*/
+typedef UInt32 OptionID;
+
+//! Option Value
+/*!
+Type to hold an option value.
+*/
+typedef SInt32 OptionValue;
+
+// for now, options are just pairs of integers
+typedef std::vector<UInt32> OptionsList;
+
+// macro for packing 4 character strings into 4 byte integers
+#define OPTION_CODE(_s) \
+ (static_cast<UInt32>(static_cast<unsigned char>(_s[0]) << 24) | \
+ static_cast<UInt32>(static_cast<unsigned char>(_s[1]) << 16) | \
+ static_cast<UInt32>(static_cast<unsigned char>(_s[2]) << 8) | \
+ static_cast<UInt32>(static_cast<unsigned char>(_s[3]) ))
+
+//! @name Option identifiers
+//@{
+static const OptionID kOptionHalfDuplexCapsLock = OPTION_CODE("HDCL");
+static const OptionID kOptionHalfDuplexNumLock = OPTION_CODE("HDNL");
+static const OptionID kOptionHalfDuplexScrollLock = OPTION_CODE("HDSL");
+static const OptionID kOptionModifierMapForShift = OPTION_CODE("MMFS");
+static const OptionID kOptionModifierMapForControl = OPTION_CODE("MMFC");
+static const OptionID kOptionModifierMapForAlt = OPTION_CODE("MMFA");
+static const OptionID kOptionModifierMapForAltGr = OPTION_CODE("MMFG");
+static const OptionID kOptionModifierMapForMeta = OPTION_CODE("MMFM");
+static const OptionID kOptionModifierMapForSuper = OPTION_CODE("MMFR");
+static const OptionID kOptionHeartbeat = OPTION_CODE("HART");
+static const OptionID kOptionScreenSwitchCorners = OPTION_CODE("SSCM");
+static const OptionID kOptionScreenSwitchCornerSize = OPTION_CODE("SSCS");
+static const OptionID kOptionScreenSwitchDelay = OPTION_CODE("SSWT");
+static const OptionID kOptionScreenSwitchTwoTap = OPTION_CODE("SSTT");
+static const OptionID kOptionScreenSwitchNeedsShift = OPTION_CODE("SSNS");
+static const OptionID kOptionScreenSwitchNeedsControl = OPTION_CODE("SSNC");
+static const OptionID kOptionScreenSwitchNeedsAlt = OPTION_CODE("SSNA");
+static const OptionID kOptionScreenSaverSync = OPTION_CODE("SSVR");
+static const OptionID kOptionXTestXineramaUnaware = OPTION_CODE("XTXU");
+static const OptionID kOptionScreenPreserveFocus = OPTION_CODE("SFOC");
+static const OptionID kOptionRelativeMouseMoves = OPTION_CODE("MDLT");
+static const OptionID kOptionWin32KeepForeground = OPTION_CODE("_KFW");
+static const OptionID kOptionClipboardSharing = OPTION_CODE("CLPS");
+//@}
+
+//! @name Screen switch corner enumeration
+//@{
+enum EScreenSwitchCorners {
+ kNoCorner,
+ kTopLeft,
+ kTopRight,
+ kBottomLeft,
+ kBottomRight,
+ kFirstCorner = kTopLeft,
+ kLastCorner = kBottomRight
+};
+//@}
+
+//! @name Screen switch corner masks
+//@{
+enum EScreenSwitchCornerMasks {
+ kNoCornerMask = 0,
+ kTopLeftMask = 1 << (kTopLeft - kFirstCorner),
+ kTopRightMask = 1 << (kTopRight - kFirstCorner),
+ kBottomLeftMask = 1 << (kBottomLeft - kFirstCorner),
+ kBottomRightMask = 1 << (kBottomRight - kFirstCorner),
+ kAllCornersMask = kTopLeftMask | kTopRightMask |
+ kBottomLeftMask | kBottomRightMask
+};
+//@}
+
+#undef OPTION_CODE
diff --git a/src/lib/barrier/protocol_types.cpp b/src/lib/barrier/protocol_types.cpp
new file mode 100644
index 0000000..07acf61
--- /dev/null
+++ b/src/lib/barrier/protocol_types.cpp
@@ -0,0 +1,53 @@
+/*
+ * barrier -- 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 "barrier/protocol_types.h"
+
+const char* kMsgHello = "Barrier%2i%2i";
+const char* kMsgHelloBack = "Barrier%2i%2i%s";
+const char* kMsgCNoop = "CNOP";
+const char* kMsgCClose = "CBYE";
+const char* kMsgCEnter = "CINN%2i%2i%4i%2i";
+const char* kMsgCLeave = "COUT";
+const char* kMsgCClipboard = "CCLP%1i%4i";
+const char* kMsgCScreenSaver = "CSEC%1i";
+const char* kMsgCResetOptions = "CROP";
+const char* kMsgCInfoAck = "CIAK";
+const char* kMsgCKeepAlive = "CALV";
+const char* kMsgDKeyDown = "DKDN%2i%2i%2i";
+const char* kMsgDKeyDown1_0 = "DKDN%2i%2i";
+const char* kMsgDKeyRepeat = "DKRP%2i%2i%2i%2i";
+const char* kMsgDKeyRepeat1_0 = "DKRP%2i%2i%2i";
+const char* kMsgDKeyUp = "DKUP%2i%2i%2i";
+const char* kMsgDKeyUp1_0 = "DKUP%2i%2i";
+const char* kMsgDMouseDown = "DMDN%1i";
+const char* kMsgDMouseUp = "DMUP%1i";
+const char* kMsgDMouseMove = "DMMV%2i%2i";
+const char* kMsgDMouseRelMove = "DMRM%2i%2i";
+const char* kMsgDMouseWheel = "DMWM%2i%2i";
+const char* kMsgDMouseWheel1_0 = "DMWM%2i";
+const char* kMsgDClipboard = "DCLP%1i%4i%1i%s";
+const char* kMsgDInfo = "DINF%2i%2i%2i%2i%2i%2i%2i";
+const char* kMsgDSetOptions = "DSOP%4I";
+const char* kMsgDFileTransfer = "DFTR%1i%s";
+const char* kMsgDDragInfo = "DDRG%2i%s";
+const char* kMsgQInfo = "QINF";
+const char* kMsgEIncompatible = "EICV%2i%2i";
+const char* kMsgEBusy = "EBSY";
+const char* kMsgEUnknown = "EUNK";
+const char* kMsgEBad = "EBAD";
diff --git a/src/lib/barrier/protocol_types.h b/src/lib/barrier/protocol_types.h
new file mode 100644
index 0000000..bc5e037
--- /dev/null
+++ b/src/lib/barrier/protocol_types.h
@@ -0,0 +1,337 @@
+/*
+ * barrier -- 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/EventTypes.h"
+
+// protocol version number
+// 1.0: initial protocol
+// 1.1: adds KeyCode to key press, release, and repeat
+// 1.2: adds mouse relative motion
+// 1.3: adds keep alive and deprecates heartbeats,
+// adds horizontal mouse scrolling
+// 1.4: adds crypto support
+// 1.5: adds file transfer and removes home brew crypto
+// 1.6: adds clipboard streaming
+// NOTE: with new version, barrier minor version should increment
+static const SInt16 kProtocolMajorVersion = 1;
+static const SInt16 kProtocolMinorVersion = 6;
+
+// default contact port number
+static const UInt16 kDefaultPort = 24800;
+
+// maximum total length for greeting returned by client
+static const UInt32 kMaxHelloLength = 1024;
+
+// time between kMsgCKeepAlive (in seconds). a non-positive value disables
+// keep alives. this is the default rate that can be overridden using an
+// option.
+static const double kKeepAliveRate = 3.0;
+
+// number of skipped kMsgCKeepAlive messages that indicates a problem
+static const double kKeepAlivesUntilDeath = 3.0;
+
+// obsolete heartbeat stuff
+static const double kHeartRate = -1.0;
+static const double kHeartBeatsUntilDeath = 3.0;
+
+// direction constants
+enum EDirection {
+ kNoDirection,
+ kLeft,
+ kRight,
+ kTop,
+ kBottom,
+ kFirstDirection = kLeft,
+ kLastDirection = kBottom,
+ kNumDirections = kLastDirection - kFirstDirection + 1
+};
+enum EDirectionMask {
+ kNoDirMask = 0,
+ kLeftMask = 1 << kLeft,
+ kRightMask = 1 << kRight,
+ kTopMask = 1 << kTop,
+ kBottomMask = 1 << kBottom
+};
+
+// Data transfer constants
+enum EDataTransfer {
+ kDataStart = 1,
+ kDataChunk = 2,
+ kDataEnd = 3
+};
+
+// Data received constants
+enum EDataReceived {
+ kStart,
+ kNotFinish,
+ kFinish,
+ kError
+};
+
+//
+// message codes (trailing NUL is not part of code). in comments, $n
+// refers to the n'th argument (counting from one). message codes are
+// always 4 bytes optionally followed by message specific parameters
+// except those for the greeting handshake.
+//
+
+//
+// positions and sizes are signed 16 bit integers.
+//
+
+//
+// greeting handshake messages
+//
+
+// say hello to client; primary -> secondary
+// $1 = protocol major version number supported by server. $2 =
+// protocol minor version number supported by server.
+extern const char* 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.
+extern const char* kMsgHelloBack;
+
+
+//
+// command codes
+//
+
+// no operation; secondary -> primary
+extern const char* kMsgCNoop;
+
+// close connection; primary -> secondary
+extern const char* kMsgCClose;
+
+// enter screen: primary -> secondary
+// entering screen at screen position $1 = x, $2 = y. x,y are
+// absolute screen coordinates. $3 = sequence number, which is
+// used to order messages between screens. the secondary screen
+// must return this number with some messages. $4 = modifier key
+// mask. this will have bits set for each toggle modifier key
+// that is activated on entry to the screen. the secondary screen
+// should adjust its toggle modifiers to reflect that state.
+extern const char* kMsgCEnter;
+
+// leave screen: primary -> secondary
+// leaving screen. the secondary screen should send clipboard
+// data in response to this message for those clipboards that
+// it has grabbed (i.e. has sent a kMsgCClipboard for and has
+// not received a kMsgCClipboard for with a greater sequence
+// number) and that were grabbed or have changed since the
+// last leave.
+extern const char* kMsgCLeave;
+
+// grab clipboard: primary <-> secondary
+// sent by screen when some other app on that screen grabs a
+// clipboard. $1 = the clipboard identifier, $2 = sequence number.
+// secondary screens must use the sequence number passed in the
+// most recent kMsgCEnter. the primary always sends 0.
+extern const char* kMsgCClipboard;
+
+// screensaver change: primary -> secondary
+// screensaver on primary has started ($1 == 1) or closed ($1 == 0)
+extern const char* kMsgCScreenSaver;
+
+// reset options: primary -> secondary
+// client should reset all of its options to their defaults.
+extern const char* kMsgCResetOptions;
+
+// resolution change acknowledgment: primary -> secondary
+// sent by primary in response to a secondary screen's kMsgDInfo.
+// this is sent for every kMsgDInfo, whether or not the primary
+// had sent a kMsgQInfo.
+extern const char* kMsgCInfoAck;
+
+// keep connection alive: primary <-> secondary
+// sent by the server periodically to verify that connections are still
+// up and running. clients must reply in kind on receipt. if the server
+// gets an error sending the message or does not receive a reply within
+// a reasonable time then the server disconnects the client. if the
+// client doesn't receive these (or any message) periodically then it
+// should disconnect from the server. the appropriate interval is
+// defined by an option.
+extern const char* kMsgCKeepAlive;
+
+//
+// data codes
+//
+
+// key pressed: primary -> secondary
+// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton
+// 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
+// release, the secondary can then use the primary's KeyButton to
+// find its corresponding physical key and release it. this is
+// necessary because the KeyID on release may not be the KeyID of
+// 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.
+extern const char* kMsgDKeyDown;
+
+// key pressed 1.0: same as above but without KeyButton
+extern const char* kMsgDKeyDown1_0;
+
+// key auto-repeat: primary -> secondary
+// $1 = KeyID, $2 = KeyModifierMask, $3 = number of repeats, $4 = KeyButton
+extern const char* kMsgDKeyRepeat;
+
+// key auto-repeat 1.0: same as above but without KeyButton
+extern const char* kMsgDKeyRepeat1_0;
+
+// key released: primary -> secondary
+// $1 = KeyID, $2 = KeyModifierMask, $3 = KeyButton
+extern const char* kMsgDKeyUp;
+
+// key released 1.0: same as above but without KeyButton
+extern const char* kMsgDKeyUp1_0;
+
+// mouse button pressed: primary -> secondary
+// $1 = ButtonID
+extern const char* kMsgDMouseDown;
+
+// mouse button released: primary -> secondary
+// $1 = ButtonID
+extern const char* kMsgDMouseUp;
+
+// mouse moved: primary -> secondary
+// $1 = x, $2 = y. x,y are absolute screen coordinates.
+extern const char* kMsgDMouseMove;
+
+// relative mouse move: primary -> secondary
+// $1 = dx, $2 = dy. dx,dy are motion deltas.
+extern const char* kMsgDMouseRelMove;
+
+// mouse scroll: primary -> secondary
+// $1 = xDelta, $2 = yDelta. the delta should be +120 for one tick forward
+// (away from the user) or right and -120 for one tick backward (toward
+// the user) or left.
+extern const char* kMsgDMouseWheel;
+
+// mouse vertical scroll: primary -> secondary
+// like as kMsgDMouseWheel except only sends $1 = yDelta.
+extern const char* kMsgDMouseWheel1_0;
+
+// clipboard data: primary <-> secondary
+// $2 = sequence number, $3 = mark $4 = clipboard data. the sequence number
+// is 0 when sent by the primary. secondary screens should use the
+// sequence number from the most recent kMsgCEnter. $1 = clipboard
+// identifier.
+extern const char* kMsgDClipboard;
+
+// client data: secondary -> primary
+// $1 = coordinate of leftmost pixel on secondary screen,
+// $2 = coordinate of topmost pixel on secondary screen,
+// $3 = width of secondary screen in pixels,
+// $4 = height of secondary screen in pixels,
+// $5 = size of warp zone, (obsolete)
+// $6, $7 = the x,y position of the mouse on the secondary screen.
+//
+// the secondary screen must send this message in response to the
+// kMsgQInfo message. it must also send this message when the
+// screen's resolution changes. in this case, the secondary screen
+// should ignore any kMsgDMouseMove messages until it receives a
+// kMsgCInfoAck in order to prevent attempts to move the mouse off
+// the new screen area.
+extern const char* kMsgDInfo;
+
+// set options: primary -> secondary
+// client should set the given option/value pairs. $1 = option/value
+// pairs.
+extern const char* kMsgDSetOptions;
+
+// file data: primary <-> secondary
+// transfer file data. A mark is used in the first byte.
+// 0 means the content followed is the file size.
+// 1 means the content followed is the chunk data.
+// 2 means the file transfer is finished.
+extern const char* kMsgDFileTransfer;
+
+// drag infomation: primary <-> secondary
+// transfer drag infomation. The first 2 bytes are used for storing
+// the number of dragging objects. Then the following string consists
+// of each object's directory.
+extern const char* kMsgDDragInfo;
+
+//
+// query codes
+//
+
+// query screen info: primary -> secondary
+// client should reply with a kMsgDInfo.
+extern const char* kMsgQInfo;
+
+
+//
+// error codes
+//
+
+// incompatible versions: primary -> secondary
+// $1 = major version of primary, $2 = minor version of primary.
+extern const char* kMsgEIncompatible;
+
+// name provided when connecting is already in use: primary -> secondary
+extern const char* kMsgEBusy;
+
+// unknown client: primary -> secondary
+// name provided when connecting is not in primary's screen
+// configuration map.
+extern const char* kMsgEUnknown;
+
+// protocol violation: primary -> secondary
+// primary should disconnect after sending this message.
+extern const char* kMsgEBad;
+
+
+//
+// structures
+//
+
+//! Screen information
+/*!
+This class contains information about a screen.
+*/
+class ClientInfo {
+public:
+ //! Screen position
+ /*!
+ The position of the upper-left corner of the screen. This is
+ typically 0,0.
+ */
+ SInt32 m_x, m_y;
+
+ //! Screen size
+ /*!
+ The size of the screen in pixels.
+ */
+ SInt32 m_w, m_h;
+
+ //! Obsolete (jump zone size)
+ SInt32 obsolete1;
+
+ //! Mouse position
+ /*!
+ The current location of the mouse cursor.
+ */
+ SInt32 m_mx, m_my;
+};
diff --git a/src/lib/barrier/unix/AppUtilUnix.cpp b/src/lib/barrier/unix/AppUtilUnix.cpp
new file mode 100644
index 0000000..a1548d8
--- /dev/null
+++ b/src/lib/barrier/unix/AppUtilUnix.cpp
@@ -0,0 +1,46 @@
+/*
+ * barrier -- 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 "barrier/unix/AppUtilUnix.h"
+#include "barrier/ArgsBase.h"
+
+AppUtilUnix::AppUtilUnix(IEventQueue* events)
+{
+}
+
+AppUtilUnix::~AppUtilUnix()
+{
+}
+
+int
+standardStartupStatic(int argc, char** argv)
+{
+ return AppUtil::instance().app().standardStartup(argc, argv);
+}
+
+int
+AppUtilUnix::run(int argc, char** argv)
+{
+ return app().runInner(argc, argv, NULL, &standardStartupStatic);
+}
+
+void
+AppUtilUnix::startNode()
+{
+ app().startNode();
+}
diff --git a/src/lib/barrier/unix/AppUtilUnix.h b/src/lib/barrier/unix/AppUtilUnix.h
new file mode 100644
index 0000000..fefcfea
--- /dev/null
+++ b/src/lib/barrier/unix/AppUtilUnix.h
@@ -0,0 +1,34 @@
+/*
+ * barrier -- 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 "barrier/AppUtil.h"
+
+#define ARCH_APP_UTIL AppUtilUnix
+
+class IEventQueue;
+
+class AppUtilUnix : public AppUtil {
+public:
+ AppUtilUnix(IEventQueue* events);
+ virtual ~AppUtilUnix();
+
+ int run(int argc, char** argv);
+ void startNode();
+};
diff --git a/src/lib/barrier/win32/AppUtilWindows.cpp b/src/lib/barrier/win32/AppUtilWindows.cpp
new file mode 100644
index 0000000..560b029
--- /dev/null
+++ b/src/lib/barrier/win32/AppUtilWindows.cpp
@@ -0,0 +1,185 @@
+/*
+ * barrier -- 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 "barrier/win32/AppUtilWindows.h"
+#include "barrier/Screen.h"
+#include "barrier/ArgsBase.h"
+#include "barrier/App.h"
+#include "barrier/XBarrier.h"
+#include "platform/MSWindowsScreen.h"
+#include "arch/win32/XArchWindows.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/IArchTaskBarReceiver.h"
+#include "base/Log.h"
+#include "base/log_outputters.h"
+#include "base/IEventQueue.h"
+#include "base/Event.h"
+#include "base/EventQueue.h"
+#include "common/Version.h"
+
+#include <sstream>
+#include <iostream>
+#include <conio.h>
+#include <VersionHelpers.h>
+
+AppUtilWindows::AppUtilWindows(IEventQueue* events) :
+ m_events(events),
+ m_exitMode(kExitModeNormal)
+{
+ if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)consoleHandler, TRUE) == FALSE)
+ {
+ throw XArch(new XArchEvalWindows());
+ }
+}
+
+AppUtilWindows::~AppUtilWindows()
+{
+}
+
+BOOL WINAPI AppUtilWindows::consoleHandler(DWORD)
+{
+ LOG((CLOG_INFO "got shutdown signal"));
+ IEventQueue* events = AppUtil::instance().app().getEvents();
+ events->addEvent(Event(Event::kQuit));
+ return TRUE;
+}
+
+static
+int
+mainLoopStatic()
+{
+ return AppUtil::instance().app().mainLoop();
+}
+
+int
+AppUtilWindows::daemonNTMainLoop(int argc, const char** argv)
+{
+ app().initApp(argc, argv);
+ debugServiceWait();
+
+ // NB: what the hell does this do?!
+ app().argsBase().m_backend = false;
+
+ return ArchMiscWindows::runDaemon(mainLoopStatic);
+}
+
+void
+AppUtilWindows::exitApp(int code)
+{
+ switch (m_exitMode) {
+
+ case kExitModeDaemon:
+ ArchMiscWindows::daemonFailed(code);
+ break;
+
+ default:
+ throw XExitApp(code);
+ }
+}
+
+int daemonNTMainLoopStatic(int argc, const char** argv)
+{
+ return AppUtilWindows::instance().daemonNTMainLoop(argc, argv);
+}
+
+int
+AppUtilWindows::daemonNTStartup(int, char**)
+{
+ SystemLogger sysLogger(app().daemonName(), false);
+ m_exitMode = kExitModeDaemon;
+ return ARCH->daemonize(app().daemonName(), daemonNTMainLoopStatic);
+}
+
+static
+int
+daemonNTStartupStatic(int argc, char** argv)
+{
+ return AppUtilWindows::instance().daemonNTStartup(argc, argv);
+}
+
+static
+int
+foregroundStartupStatic(int argc, char** argv)
+{
+ return AppUtil::instance().app().foregroundStartup(argc, argv);
+}
+
+void
+AppUtilWindows::beforeAppExit()
+{
+ // this can be handy for debugging, since the application is launched in
+ // a new console window, and will normally close on exit (making it so
+ // that we can't see error messages).
+ if (app().argsBase().m_pauseOnExit) {
+ std::cout << std::endl << "press any key to exit..." << std::endl;
+ int c = _getch();
+ }
+}
+
+int
+AppUtilWindows::run(int argc, char** argv)
+{
+ if (!IsWindowsXPSP3OrGreater()) {
+ throw std::runtime_error("Barrier only supports Windows XP SP3 and above.");
+ }
+
+ // record window instance for tray icon, etc
+ ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
+
+ MSWindowsScreen::init(ArchMiscWindows::instanceWin32());
+ Thread::getCurrentThread().setPriority(-14);
+
+ StartupFunc startup;
+ if (ArchMiscWindows::wasLaunchedAsService()) {
+ startup = &daemonNTStartupStatic;
+ } else {
+ startup = &foregroundStartupStatic;
+ app().argsBase().m_daemon = false;
+ }
+
+ return app().runInner(argc, argv, NULL, startup);
+}
+
+AppUtilWindows&
+AppUtilWindows::instance()
+{
+ return (AppUtilWindows&)AppUtil::instance();
+}
+
+void
+AppUtilWindows::debugServiceWait()
+{
+ if (app().argsBase().m_debugServiceWait)
+ {
+ while(true)
+ {
+ // this code is only executed when the process is launched via the
+ // windows service controller (and --debug-service-wait arg is
+ // used). to debug, set a breakpoint on this line so that
+ // execution is delayed until the debugger is attached.
+ ARCH->sleep(1);
+ LOG((CLOG_INFO "waiting for debugger to attach"));
+ }
+ }
+}
+
+void
+AppUtilWindows::startNode()
+{
+ app().startNode();
+}
diff --git a/src/lib/barrier/win32/AppUtilWindows.h b/src/lib/barrier/win32/AppUtilWindows.h
new file mode 100644
index 0000000..c5da228
--- /dev/null
+++ b/src/lib/barrier/win32/AppUtilWindows.h
@@ -0,0 +1,62 @@
+/*
+ * barrier -- 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 "barrier/AppUtil.h"
+#include "base/String.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include "Windows.h"
+
+#define ARCH_APP_UTIL AppUtilWindows
+
+class IEventQueue;
+
+enum AppExitMode {
+ kExitModeNormal,
+ kExitModeDaemon
+};
+
+class AppUtilWindows : public AppUtil {
+public:
+ AppUtilWindows(IEventQueue* events);
+ virtual ~AppUtilWindows();
+
+ int daemonNTStartup(int, char**);
+
+ int daemonNTMainLoop(int argc, const char** argv);
+
+ void debugServiceWait();
+
+ int run(int argc, char** argv);
+
+ void exitApp(int code);
+
+ void beforeAppExit();
+
+ static AppUtilWindows& instance();
+
+ void startNode();
+
+private:
+ AppExitMode m_exitMode;
+ IEventQueue* m_events;
+
+ static BOOL WINAPI consoleHandler(DWORD Event);
+};
diff --git a/src/lib/barrier/win32/DaemonApp.cpp b/src/lib/barrier/win32/DaemonApp.cpp
new file mode 100644
index 0000000..62fecf8
--- /dev/null
+++ b/src/lib/barrier/win32/DaemonApp.cpp
@@ -0,0 +1,361 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "barrier/win32/DaemonApp.h"
+
+#include "barrier/App.h"
+#include "barrier/ArgParser.h"
+#include "barrier/ServerArgs.h"
+#include "barrier/ClientArgs.h"
+#include "ipc/IpcClientProxy.h"
+#include "ipc/IpcMessage.h"
+#include "ipc/IpcLogOutputter.h"
+#include "net/SocketMultiplexer.h"
+#include "arch/XArch.h"
+#include "base/Log.h"
+#include "base/TMethodJob.h"
+#include "base/TMethodEventJob.h"
+#include "base/EventQueue.h"
+#include "base/log_outputters.h"
+#include "base/Log.h"
+
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/win32/XArchWindows.h"
+#include "barrier/Screen.h"
+#include "platform/MSWindowsScreen.h"
+#include "platform/MSWindowsDebugOutputter.h"
+#include "platform/MSWindowsWatchdog.h"
+#include "platform/MSWindowsEventQueueBuffer.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#include <string>
+#include <iostream>
+#include <sstream>
+
+using namespace std;
+
+DaemonApp* DaemonApp::s_instance = NULL;
+
+int
+mainLoopStatic()
+{
+ DaemonApp::s_instance->mainLoop(true);
+ return kExitSuccess;
+}
+
+int
+mainLoopStatic(int, const char**)
+{
+ return ArchMiscWindows::runDaemon(mainLoopStatic);
+}
+
+DaemonApp::DaemonApp() :
+ m_ipcServer(nullptr),
+ m_ipcLogOutputter(nullptr),
+ m_watchdog(nullptr),
+ m_events(nullptr),
+ m_fileLogOutputter(nullptr)
+{
+ s_instance = this;
+}
+
+DaemonApp::~DaemonApp()
+{
+}
+
+int
+DaemonApp::run(int argc, char** argv)
+{
+ // win32 instance needed for threading, etc.
+ ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
+
+ Arch arch;
+ arch.init();
+
+ Log log;
+ EventQueue events;
+ m_events = &events;
+
+ bool uninstall = false;
+ try
+ {
+ // sends debug messages to visual studio console window.
+ log.insert(new MSWindowsDebugOutputter());
+
+ // default log level to system setting.
+ string logLevel = arch.setting("LogLevel");
+ if (logLevel != "")
+ log.setFilter(logLevel.c_str());
+
+ bool foreground = false;
+
+ for (int i = 1; i < argc; ++i) {
+ string arg(argv[i]);
+
+ if (arg == "/f" || arg == "-f") {
+ foreground = true;
+ }
+ else if (arg == "/install") {
+ uninstall = true;
+ arch.installDaemon();
+ return kExitSuccess;
+ }
+ else if (arg == "/uninstall") {
+ arch.uninstallDaemon();
+ return kExitSuccess;
+ }
+ else {
+ stringstream ss;
+ ss << "Unrecognized argument: " << arg;
+ foregroundError(ss.str().c_str());
+ return kExitArgs;
+ }
+ }
+
+ if (foreground) {
+ // run process in foreground instead of daemonizing.
+ // useful for debugging.
+ mainLoop(false);
+ }
+ else {
+ arch.daemonize("Barrier", mainLoopStatic);
+ }
+
+ return kExitSuccess;
+ }
+ catch (XArch& e) {
+ String message = e.what();
+ if (uninstall && (message.find("The service has not been started") != String::npos)) {
+ // TODO: if we're keeping this use error code instead (what is it?!).
+ // HACK: this message happens intermittently, not sure where from but
+ // it's quite misleading for the user. they thing something has gone
+ // horribly wrong, but it's just the service manager reporting a false
+ // positive (the service has actually shut down in most cases).
+ }
+ else {
+ foregroundError(message.c_str());
+ }
+ return kExitFailed;
+ }
+ catch (std::exception& e) {
+ foregroundError(e.what());
+ return kExitFailed;
+ }
+ catch (...) {
+ foregroundError("Unrecognized error.");
+ return kExitFailed;
+ }
+}
+
+void
+DaemonApp::mainLoop(bool daemonized)
+{
+ try
+ {
+ DAEMON_RUNNING(true);
+
+ if (daemonized) {
+ m_fileLogOutputter = new FileLogOutputter(logFilename().c_str());
+ CLOG->insert(m_fileLogOutputter);
+ }
+
+ // create socket multiplexer. this must happen after daemonization
+ // on unix because threads evaporate across a fork().
+ SocketMultiplexer multiplexer;
+
+ // uses event queue, must be created here.
+ m_ipcServer = new IpcServer(m_events, &multiplexer);
+
+ // send logging to gui via ipc, log system adopts outputter.
+ m_ipcLogOutputter = new IpcLogOutputter(*m_ipcServer, kIpcClientGui, true);
+ CLOG->insert(m_ipcLogOutputter);
+
+ m_watchdog = new MSWindowsWatchdog(daemonized, false, *m_ipcServer, *m_ipcLogOutputter);
+ m_watchdog->setFileLogOutputter(m_fileLogOutputter);
+
+ m_events->adoptHandler(
+ m_events->forIpcServer().messageReceived(), m_ipcServer,
+ new TMethodEventJob<DaemonApp>(this, &DaemonApp::handleIpcMessage));
+
+ m_ipcServer->listen();
+
+ // install the platform event queue to handle service stop events.
+ m_events->adoptBuffer(new MSWindowsEventQueueBuffer(m_events));
+
+ String command = ARCH->setting("Command");
+ bool elevate = ARCH->setting("Elevate") == "1";
+ if (command != "") {
+ LOG((CLOG_INFO "using last known command: %s", command.c_str()));
+ m_watchdog->setCommand(command, elevate);
+ }
+
+ m_watchdog->startAsync();
+
+ m_events->loop();
+
+ m_watchdog->stop();
+ delete m_watchdog;
+
+ m_events->removeHandler(
+ m_events->forIpcServer().messageReceived(), m_ipcServer);
+
+ CLOG->remove(m_ipcLogOutputter);
+ delete m_ipcLogOutputter;
+ delete m_ipcServer;
+
+ DAEMON_RUNNING(false);
+ }
+ catch (std::exception& e) {
+ LOG((CLOG_CRIT "An error occurred: %s", e.what()));
+ }
+ catch (...) {
+ LOG((CLOG_CRIT "An unknown error occurred.\n"));
+ }
+}
+
+void
+DaemonApp::foregroundError(const char* message)
+{
+ MessageBox(NULL, message, "Barrier Service", MB_OK | MB_ICONERROR);
+}
+
+std::string
+DaemonApp::logFilename()
+{
+ string logFilename;
+ logFilename = ARCH->setting("LogFilename");
+ if (logFilename.empty()) {
+ logFilename = ARCH->getLogDirectory();
+ logFilename.append("/");
+ logFilename.append(LOG_FILENAME);
+ }
+
+ return logFilename;
+}
+
+void
+DaemonApp::handleIpcMessage(const Event& e, void*)
+{
+ IpcMessage* m = static_cast<IpcMessage*>(e.getDataObject());
+ switch (m->type()) {
+ case kIpcCommand: {
+ IpcCommandMessage* cm = static_cast<IpcCommandMessage*>(m);
+ String command = cm->command();
+
+ // if empty quotes, clear.
+ if (command == "\"\"") {
+ command.clear();
+ }
+
+ if (!command.empty()) {
+ LOG((CLOG_DEBUG "new command, elevate=%d command=%s", cm->elevate(), command.c_str()));
+
+ std::vector<String> argsArray;
+ ArgParser::splitCommandString(command, argsArray);
+ ArgParser argParser(NULL);
+ const char** argv = argParser.getArgv(argsArray);
+ ServerArgs serverArgs;
+ ClientArgs clientArgs;
+ int argc = static_cast<int>(argsArray.size());
+ bool server = argsArray[0].find("barriers") != String::npos ? true : false;
+ ArgsBase* argBase = NULL;
+
+ if (server) {
+ argParser.parseServerArgs(serverArgs, argc, argv);
+ argBase = &serverArgs;
+ }
+ else {
+ argParser.parseClientArgs(clientArgs, argc, argv);
+ argBase = &clientArgs;
+ }
+
+ delete[] argv;
+
+ String logLevel(argBase->m_logFilter);
+ if (!logLevel.empty()) {
+ try {
+ // change log level based on that in the command string
+ // and change to that log level now.
+ ARCH->setting("LogLevel", logLevel);
+ CLOG->setFilter(logLevel.c_str());
+ }
+ catch (XArch& e) {
+ LOG((CLOG_ERR "failed to save LogLevel setting, %s", e.what()));
+ }
+ }
+
+ // eg. no log-to-file while running in foreground
+ if (m_fileLogOutputter != nullptr) {
+ String logFilename;
+ if (argBase->m_logFile != NULL) {
+ logFilename = String(argBase->m_logFile);
+ ARCH->setting("LogFilename", logFilename);
+ m_watchdog->setFileLogOutputter(m_fileLogOutputter);
+ command = ArgParser::assembleCommand(argsArray, "--log", 1);
+ LOG((CLOG_DEBUG "removed log file argument and filename %s from command ", logFilename.c_str()));
+ LOG((CLOG_DEBUG "new command, elevate=%d command=%s", cm->elevate(), command.c_str()));
+ } else {
+ m_watchdog->setFileLogOutputter(NULL);
+ }
+ m_fileLogOutputter->setLogFilename(logFilename.c_str());
+ }
+ }
+ else {
+ LOG((CLOG_DEBUG "empty command, elevate=%d", cm->elevate()));
+ }
+
+ try {
+ // store command in system settings. this is used when the daemon
+ // next starts.
+ ARCH->setting("Command", command);
+
+ // TODO: it would be nice to store bools/ints...
+ ARCH->setting("Elevate", String(cm->elevate() ? "1" : "0"));
+ }
+ catch (XArch& e) {
+ LOG((CLOG_ERR "failed to save settings, %s", e.what()));
+ }
+
+ // tell the relauncher about the new command. this causes the
+ // relauncher to stop the existing command and start the new
+ // command.
+ m_watchdog->setCommand(command, cm->elevate());
+
+ break;
+ }
+
+ case kIpcHello:
+ IpcHelloMessage* hm = static_cast<IpcHelloMessage*>(m);
+ String type;
+ switch (hm->clientType()) {
+ case kIpcClientGui: type = "gui"; break;
+ case kIpcClientNode: type = "node"; break;
+ default: type = "unknown"; break;
+ }
+
+ LOG((CLOG_DEBUG "ipc hello, type=%s", type.c_str()));
+
+ const char * serverstatus = m_watchdog->isProcessActive() ? "active" : "not active";
+ LOG((CLOG_INFO "server status: %s", serverstatus));
+
+ m_ipcLogOutputter->notifyBuffer();
+ break;
+ }
+}
diff --git a/src/lib/barrier/win32/DaemonApp.h b/src/lib/barrier/win32/DaemonApp.h
new file mode 100644
index 0000000..2a8484b
--- /dev/null
+++ b/src/lib/barrier/win32/DaemonApp.h
@@ -0,0 +1,58 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "arch/Arch.h"
+#include "ipc/IpcServer.h"
+
+#include <string>
+
+class Event;
+class IpcLogOutputter;
+class FileLogOutputter;
+
+class MSWindowsWatchdog;
+
+class DaemonApp {
+
+public:
+ DaemonApp();
+ virtual ~DaemonApp();
+ int run(int argc, char** argv);
+ void mainLoop(bool daemonized);
+
+private:
+ void daemonize();
+ void foregroundError(const char* message);
+ std::string logFilename();
+ void handleIpcMessage(const Event&, void*);
+
+public:
+ static DaemonApp* s_instance;
+
+ MSWindowsWatchdog* m_watchdog;
+
+private:
+ IpcServer* m_ipcServer;
+ IpcLogOutputter* m_ipcLogOutputter;
+ IEventQueue* m_events;
+ FileLogOutputter* m_fileLogOutputter;
+};
+
+#define LOG_FILENAME "barrierd.log"
diff --git a/src/lib/base/CMakeLists.txt b/src/lib/base/CMakeLists.txt
new file mode 100644
index 0000000..66ba5a6
--- /dev/null
+++ b/src/lib/base/CMakeLists.txt
@@ -0,0 +1,28 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(base STATIC ${sources})
+
+if (UNIX)
+ target_link_libraries(base common)
+endif()
diff --git a/src/lib/base/ELevel.h b/src/lib/base/ELevel.h
new file mode 100644
index 0000000..ec0f94f
--- /dev/null
+++ b/src/lib/base/ELevel.h
@@ -0,0 +1,38 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 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
+
+//! Log levels
+/*!
+The logging priority levels in order of highest to lowest priority.
+*/
+enum ELevel {
+ kPRINT = -1, //!< For print only (no file or time)
+ kFATAL, //!< For fatal errors
+ kERROR, //!< For serious errors
+ kWARNING, //!< For minor errors and warnings
+ kNOTE, //!< For messages about notable events
+ kINFO, //!< For informational messages
+ kDEBUG, //!< For important debugging messages
+ kDEBUG1, //!< For verbosity +1 debugging messages
+ kDEBUG2, //!< For verbosity +2 debugging messages
+ kDEBUG3, //!< For verbosity +3 debugging messages
+ kDEBUG4, //!< For verbosity +4 debugging messages
+ kDEBUG5 //!< For verbosity +5 debugging messages
+};
diff --git a/src/lib/base/Event.cpp b/src/lib/base/Event.cpp
new file mode 100644
index 0000000..f2c1a12
--- /dev/null
+++ b/src/lib/base/Event.cpp
@@ -0,0 +1,100 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "base/Event.h"
+#include "base/EventQueue.h"
+
+//
+// Event
+//
+
+Event::Event() :
+ m_type(kUnknown),
+ m_target(NULL),
+ m_data(NULL),
+ m_flags(0),
+ m_dataObject(nullptr)
+{
+ // do nothing
+}
+
+Event::Event(Type type, void* target, void* data, Flags flags) :
+ m_type(type),
+ m_target(target),
+ m_data(data),
+ m_flags(flags),
+ m_dataObject(nullptr)
+{
+ // do nothing
+}
+
+Event::Type
+Event::getType() const
+{
+ return m_type;
+}
+
+void*
+Event::getTarget() const
+{
+ return m_target;
+}
+
+void*
+Event::getData() const
+{
+ return m_data;
+}
+
+EventData*
+Event::getDataObject() const
+{
+ return m_dataObject;
+}
+
+Event::Flags
+Event::getFlags() const
+{
+ return m_flags;
+}
+
+void
+Event::deleteData(const Event& event)
+{
+ switch (event.getType()) {
+ case kUnknown:
+ case kQuit:
+ case kSystem:
+ case kTimer:
+ break;
+
+ default:
+ if ((event.getFlags() & kDontFreeData) == 0) {
+ free(event.getData());
+ delete event.getDataObject();
+ }
+ break;
+ }
+}
+
+void
+Event::setDataObject(EventData* dataObject)
+{
+ assert(m_dataObject == nullptr);
+ m_dataObject = dataObject;
+}
diff --git a/src/lib/base/Event.h b/src/lib/base/Event.h
new file mode 100644
index 0000000..2741813
--- /dev/null
+++ b/src/lib/base/Event.h
@@ -0,0 +1,126 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "common/basic_types.h"
+#include "common/stdmap.h"
+
+class EventData {
+public:
+ EventData() { }
+ virtual ~EventData() { }
+};
+
+//! Event
+/*!
+A \c Event holds an event type and a pointer to event data.
+*/
+class Event {
+public:
+ typedef UInt32 Type;
+ enum {
+ kUnknown, //!< The event type is unknown
+ kQuit, //!< The quit event
+ kSystem, //!< The data points to a system event type
+ kTimer, //!< The data points to timer info
+ kLast //!< Must be last
+ };
+
+ typedef UInt32 Flags;
+ enum {
+ kNone = 0x00, //!< No flags
+ kDeliverImmediately = 0x01, //!< Dispatch and free event immediately
+ kDontFreeData = 0x02 //!< Don't free data in deleteData
+ };
+
+ Event();
+
+ //! Create \c Event with data (POD)
+ /*!
+ The \p data must be POD (plain old data) allocated by malloc(),
+ which means it cannot have a constructor, destructor or be
+ composed of any types that do. For non-POD (normal C++ objects
+ use \c setDataObject().
+ \p target is the intended recipient of the event.
+ \p flags is any combination of \c Flags.
+ */
+ Event(Type type, void* target = NULL, void* data = NULL,
+ Flags flags = kNone);
+
+ //! @name manipulators
+ //@{
+
+ //! Release event data
+ /*!
+ Deletes event data for the given event (using free()).
+ */
+ static void deleteData(const Event&);
+
+ //! Set data (non-POD)
+ /*!
+ Set non-POD (non plain old data), where delete is called when the event
+ is deleted, and the destructor is called.
+ */
+ void setDataObject(EventData* dataObject);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get event type
+ /*!
+ Returns the event type.
+ */
+ Type getType() const;
+
+ //! Get the event target
+ /*!
+ Returns the event target.
+ */
+ void* getTarget() const;
+
+ //! Get the event data (POD).
+ /*!
+ Returns the event data (POD).
+ */
+ void* getData() const;
+
+ //! Get the event data (non-POD)
+ /*!
+ Returns the event data (non-POD). The difference between this and
+ \c getData() is that when delete is called on this data, so non-POD
+ (non plain old data) dtor is called.
+ */
+ EventData* getDataObject() const;
+
+ //! Get event flags
+ /*!
+ Returns the event flags.
+ */
+ Flags getFlags() const;
+
+ //@}
+
+private:
+ Type m_type;
+ void* m_target;
+ void* m_data;
+ Flags m_flags;
+ EventData* m_dataObject;
+};
diff --git a/src/lib/base/EventQueue.cpp b/src/lib/base/EventQueue.cpp
new file mode 100644
index 0000000..b17e35b
--- /dev/null
+++ b/src/lib/base/EventQueue.cpp
@@ -0,0 +1,658 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "base/EventQueue.h"
+
+#include "mt/Mutex.h"
+#include "mt/Lock.h"
+#include "arch/Arch.h"
+#include "base/SimpleEventQueueBuffer.h"
+#include "base/Stopwatch.h"
+#include "base/IEventJob.h"
+#include "base/EventTypes.h"
+#include "base/Log.h"
+#include "base/XBase.h"
+#include "../gui/src/ShutdownCh.h"
+
+EVENT_TYPE_ACCESSOR(Client)
+EVENT_TYPE_ACCESSOR(IStream)
+EVENT_TYPE_ACCESSOR(IpcClient)
+EVENT_TYPE_ACCESSOR(IpcClientProxy)
+EVENT_TYPE_ACCESSOR(IpcServer)
+EVENT_TYPE_ACCESSOR(IpcServerProxy)
+EVENT_TYPE_ACCESSOR(IDataSocket)
+EVENT_TYPE_ACCESSOR(IListenSocket)
+EVENT_TYPE_ACCESSOR(ISocket)
+EVENT_TYPE_ACCESSOR(OSXScreen)
+EVENT_TYPE_ACCESSOR(ClientListener)
+EVENT_TYPE_ACCESSOR(ClientProxy)
+EVENT_TYPE_ACCESSOR(ClientProxyUnknown)
+EVENT_TYPE_ACCESSOR(Server)
+EVENT_TYPE_ACCESSOR(ServerApp)
+EVENT_TYPE_ACCESSOR(IKeyState)
+EVENT_TYPE_ACCESSOR(IPrimaryScreen)
+EVENT_TYPE_ACCESSOR(IScreen)
+EVENT_TYPE_ACCESSOR(Clipboard)
+EVENT_TYPE_ACCESSOR(File)
+
+// interrupt handler. this just adds a quit event to the queue.
+static
+void
+interrupt(Arch::ESignal, void* data)
+{
+ EventQueue* events = static_cast<EventQueue*>(data);
+ events->addEvent(Event(Event::kQuit));
+}
+
+
+//
+// EventQueue
+//
+
+EventQueue::EventQueue() :
+ m_systemTarget(0),
+ m_nextType(Event::kLast),
+ m_typesForClient(NULL),
+ m_typesForIStream(NULL),
+ m_typesForIpcClient(NULL),
+ m_typesForIpcClientProxy(NULL),
+ m_typesForIpcServer(NULL),
+ m_typesForIpcServerProxy(NULL),
+ m_typesForIDataSocket(NULL),
+ m_typesForIListenSocket(NULL),
+ m_typesForISocket(NULL),
+ m_typesForOSXScreen(NULL),
+ m_typesForClientListener(NULL),
+ m_typesForClientProxy(NULL),
+ m_typesForClientProxyUnknown(NULL),
+ m_typesForServer(NULL),
+ m_typesForServerApp(NULL),
+ m_typesForIKeyState(NULL),
+ m_typesForIPrimaryScreen(NULL),
+ m_typesForIScreen(NULL),
+ m_typesForClipboard(NULL),
+ m_typesForFile(NULL),
+ m_readyMutex(new Mutex),
+ m_readyCondVar(new CondVar<bool>(m_readyMutex, false))
+{
+ m_mutex = ARCH->newMutex();
+ ARCH->setSignalHandler(Arch::kINTERRUPT, &interrupt, this);
+ ARCH->setSignalHandler(Arch::kTERMINATE, &interrupt, this);
+ m_buffer = new SimpleEventQueueBuffer;
+}
+
+EventQueue::~EventQueue()
+{
+ delete m_buffer;
+ delete m_readyCondVar;
+ delete m_readyMutex;
+
+ ARCH->setSignalHandler(Arch::kINTERRUPT, NULL, NULL);
+ ARCH->setSignalHandler(Arch::kTERMINATE, NULL, NULL);
+ ARCH->closeMutex(m_mutex);
+}
+
+void
+EventQueue::loop()
+{
+ m_buffer->init();
+ {
+ Lock lock(m_readyMutex);
+ *m_readyCondVar = true;
+ m_readyCondVar->signal();
+ }
+ LOG((CLOG_DEBUG "event queue is ready"));
+ while (!m_pending.empty()) {
+ LOG((CLOG_DEBUG "add pending events to buffer"));
+ Event& event = m_pending.front();
+ addEventToBuffer(event);
+ m_pending.pop();
+ }
+
+ Event event;
+ getEvent(event);
+ while (event.getType() != Event::kQuit) {
+ dispatchEvent(event);
+ Event::deleteData(event);
+ getEvent(event);
+ }
+}
+
+Event::Type
+EventQueue::registerTypeOnce(Event::Type& type, const char* name)
+{
+ ArchMutexLock lock(m_mutex);
+ if (type == Event::kUnknown) {
+ m_typeMap.insert(std::make_pair(m_nextType, name));
+ m_nameMap.insert(std::make_pair(name, m_nextType));
+ LOG((CLOG_DEBUG1 "registered event type %s as %d", name, m_nextType));
+ type = m_nextType++;
+ }
+ return type;
+}
+
+const char*
+EventQueue::getTypeName(Event::Type type)
+{
+ switch (type) {
+ case Event::kUnknown:
+ return "nil";
+
+ case Event::kQuit:
+ return "quit";
+
+ case Event::kSystem:
+ return "system";
+
+ case Event::kTimer:
+ return "timer";
+
+ default:
+ TypeMap::const_iterator i = m_typeMap.find(type);
+ if (i == m_typeMap.end()) {
+ return "<unknown>";
+ }
+ else {
+ return i->second;
+ }
+ }
+}
+
+void
+EventQueue::adoptBuffer(IEventQueueBuffer* buffer)
+{
+ ArchMutexLock lock(m_mutex);
+
+ LOG((CLOG_DEBUG "adopting new buffer"));
+
+ if (m_events.size() != 0) {
+ // this can come as a nasty surprise to programmers expecting
+ // their events to be raised, only to have them deleted.
+ LOG((CLOG_DEBUG "discarding %d event(s)", m_events.size()));
+ }
+
+ // discard old buffer and old events
+ delete m_buffer;
+ for (EventTable::iterator i = m_events.begin(); i != m_events.end(); ++i) {
+ Event::deleteData(i->second);
+ }
+ m_events.clear();
+ m_oldEventIDs.clear();
+
+ // use new buffer
+ m_buffer = buffer;
+ if (m_buffer == NULL) {
+ m_buffer = new SimpleEventQueueBuffer;
+ }
+}
+
+bool
+EventQueue::parent_requests_shutdown() const
+{
+ char ch;
+ return m_parentStream.try_read_char(ch) && ch == ShutdownCh;
+}
+
+bool
+EventQueue::getEvent(Event& event, double timeout)
+{
+ Stopwatch timer(true);
+retry:
+ // before handling any events make sure we don't need to shutdown
+ if (parent_requests_shutdown()) {
+ event = Event(Event::kQuit);
+ return false;
+ }
+ // if no events are waiting then handle timers and then wait
+ while (m_buffer->isEmpty()) {
+ // handle timers first
+ if (hasTimerExpired(event)) {
+ return true;
+ }
+
+ // get time remaining in timeout
+ double timeLeft = timeout - timer.getTime();
+ if (timeout >= 0.0 && timeLeft <= 0.0) {
+ return false;
+ }
+
+ // get time until next timer expires. if there is a timer
+ // and it'll expire before the client's timeout then use
+ // that duration for our timeout instead.
+ double timerTimeout = getNextTimerTimeout();
+ if (timeout < 0.0 || (timerTimeout >= 0.0 && timerTimeout < timeLeft)) {
+ timeLeft = timerTimeout;
+ }
+
+ // wait for an event
+ m_buffer->waitForEvent(timeLeft);
+ }
+
+ // get the event
+ UInt32 dataID;
+ IEventQueueBuffer::Type type = m_buffer->getEvent(event, dataID);
+ switch (type) {
+ case IEventQueueBuffer::kNone:
+ if (timeout < 0.0 || timeout <= timer.getTime()) {
+ // don't want to fail if client isn't expecting that
+ // so if getEvent() fails with an infinite timeout
+ // then just try getting another event.
+ goto retry;
+ }
+ return false;
+
+ case IEventQueueBuffer::kSystem:
+ return true;
+
+ case IEventQueueBuffer::kUser:
+ {
+ ArchMutexLock lock(m_mutex);
+ event = removeEvent(dataID);
+ return true;
+ }
+
+ default:
+ assert(0 && "invalid event type");
+ return false;
+ }
+}
+
+bool
+EventQueue::dispatchEvent(const Event& event)
+{
+ void* target = event.getTarget();
+ IEventJob* job = getHandler(event.getType(), target);
+ if (job == NULL) {
+ job = getHandler(Event::kUnknown, target);
+ }
+ if (job != NULL) {
+ job->run(event);
+ return true;
+ }
+ return false;
+}
+
+void
+EventQueue::addEvent(const Event& event)
+{
+ // discard bogus event types
+ switch (event.getType()) {
+ case Event::kUnknown:
+ case Event::kSystem:
+ case Event::kTimer:
+ return;
+
+ default:
+ break;
+ }
+
+ if ((event.getFlags() & Event::kDeliverImmediately) != 0) {
+ dispatchEvent(event);
+ Event::deleteData(event);
+ }
+ else if (!(*m_readyCondVar)) {
+ m_pending.push(event);
+ }
+ else {
+ addEventToBuffer(event);
+ }
+}
+
+void
+EventQueue::addEventToBuffer(const Event& event)
+{
+ ArchMutexLock lock(m_mutex);
+
+ // store the event's data locally
+ UInt32 eventID = saveEvent(event);
+
+ // add it
+ if (!m_buffer->addEvent(eventID)) {
+ // failed to send event
+ removeEvent(eventID);
+ Event::deleteData(event);
+ }
+}
+
+EventQueueTimer*
+EventQueue::newTimer(double duration, void* target)
+{
+ assert(duration > 0.0);
+
+ EventQueueTimer* timer = m_buffer->newTimer(duration, false);
+ if (target == NULL) {
+ target = timer;
+ }
+ ArchMutexLock lock(m_mutex);
+ m_timers.insert(timer);
+ // initial duration is requested duration plus whatever's on
+ // the clock currently because the latter will be subtracted
+ // the next time we check for timers.
+ m_timerQueue.push(Timer(timer, duration,
+ duration + m_time.getTime(), target, false));
+ return timer;
+}
+
+EventQueueTimer*
+EventQueue::newOneShotTimer(double duration, void* target)
+{
+ assert(duration > 0.0);
+
+ EventQueueTimer* timer = m_buffer->newTimer(duration, true);
+ if (target == NULL) {
+ target = timer;
+ }
+ ArchMutexLock lock(m_mutex);
+ m_timers.insert(timer);
+ // initial duration is requested duration plus whatever's on
+ // the clock currently because the latter will be subtracted
+ // the next time we check for timers.
+ m_timerQueue.push(Timer(timer, duration,
+ duration + m_time.getTime(), target, true));
+ return timer;
+}
+
+void
+EventQueue::deleteTimer(EventQueueTimer* timer)
+{
+ ArchMutexLock lock(m_mutex);
+ for (TimerQueue::iterator index = m_timerQueue.begin();
+ index != m_timerQueue.end(); ++index) {
+ if (index->getTimer() == timer) {
+ m_timerQueue.erase(index);
+ break;
+ }
+ }
+ Timers::iterator index = m_timers.find(timer);
+ if (index != m_timers.end()) {
+ m_timers.erase(index);
+ }
+ m_buffer->deleteTimer(timer);
+}
+
+void
+EventQueue::adoptHandler(Event::Type type, void* target, IEventJob* handler)
+{
+ ArchMutexLock lock(m_mutex);
+ IEventJob*& job = m_handlers[target][type];
+ delete job;
+ job = handler;
+}
+
+void
+EventQueue::removeHandler(Event::Type type, void* target)
+{
+ IEventJob* handler = NULL;
+ {
+ ArchMutexLock lock(m_mutex);
+ HandlerTable::iterator index = m_handlers.find(target);
+ if (index != m_handlers.end()) {
+ TypeHandlerTable& typeHandlers = index->second;
+ TypeHandlerTable::iterator index2 = typeHandlers.find(type);
+ if (index2 != typeHandlers.end()) {
+ handler = index2->second;
+ typeHandlers.erase(index2);
+ }
+ }
+ }
+ delete handler;
+}
+
+void
+EventQueue::removeHandlers(void* target)
+{
+ std::vector<IEventJob*> handlers;
+ {
+ ArchMutexLock lock(m_mutex);
+ HandlerTable::iterator index = m_handlers.find(target);
+ if (index != m_handlers.end()) {
+ // copy to handlers array and clear table for target
+ TypeHandlerTable& typeHandlers = index->second;
+ for (TypeHandlerTable::iterator index2 = typeHandlers.begin();
+ index2 != typeHandlers.end(); ++index2) {
+ handlers.push_back(index2->second);
+ }
+ typeHandlers.clear();
+ }
+ }
+
+ // delete handlers
+ for (std::vector<IEventJob*>::iterator index = handlers.begin();
+ index != handlers.end(); ++index) {
+ delete *index;
+ }
+}
+
+bool
+EventQueue::isEmpty() const
+{
+ return (m_buffer->isEmpty() && getNextTimerTimeout() != 0.0);
+}
+
+IEventJob*
+EventQueue::getHandler(Event::Type type, void* target) const
+{
+ ArchMutexLock lock(m_mutex);
+ HandlerTable::const_iterator index = m_handlers.find(target);
+ if (index != m_handlers.end()) {
+ const TypeHandlerTable& typeHandlers = index->second;
+ TypeHandlerTable::const_iterator index2 = typeHandlers.find(type);
+ if (index2 != typeHandlers.end()) {
+ return index2->second;
+ }
+ }
+ return NULL;
+}
+
+UInt32
+EventQueue::saveEvent(const Event& event)
+{
+ // choose id
+ UInt32 id;
+ if (!m_oldEventIDs.empty()) {
+ // reuse an id
+ id = m_oldEventIDs.back();
+ m_oldEventIDs.pop_back();
+ }
+ else {
+ // make a new id
+ id = static_cast<UInt32>(m_events.size());
+ }
+
+ // save data
+ m_events[id] = event;
+ return id;
+}
+
+Event
+EventQueue::removeEvent(UInt32 eventID)
+{
+ // look up id
+ EventTable::iterator index = m_events.find(eventID);
+ if (index == m_events.end()) {
+ return Event();
+ }
+
+ // get data
+ Event event = index->second;
+ m_events.erase(index);
+
+ // save old id for reuse
+ m_oldEventIDs.push_back(eventID);
+
+ return event;
+}
+
+bool
+EventQueue::hasTimerExpired(Event& event)
+{
+ // return true if there's a timer in the timer priority queue that
+ // has expired. if returning true then fill in event appropriately
+ // and reset and reinsert the timer.
+ if (m_timerQueue.empty()) {
+ return false;
+ }
+
+ // get time elapsed since last check
+ const double time = m_time.getTime();
+ m_time.reset();
+
+ // countdown elapsed time
+ for (TimerQueue::iterator index = m_timerQueue.begin();
+ index != m_timerQueue.end(); ++index) {
+ (*index) -= time;
+ }
+
+ // done if no timers are expired
+ if (m_timerQueue.top() > 0.0) {
+ return false;
+ }
+
+ // remove timer from queue
+ Timer timer = m_timerQueue.top();
+ m_timerQueue.pop();
+
+ // prepare event and reset the timer's clock
+ timer.fillEvent(m_timerEvent);
+ event = Event(Event::kTimer, timer.getTarget(), &m_timerEvent);
+ timer.reset();
+
+ // reinsert timer into queue if it's not a one-shot
+ if (!timer.isOneShot()) {
+ m_timerQueue.push(timer);
+ }
+
+ return true;
+}
+
+double
+EventQueue::getNextTimerTimeout() const
+{
+ // return -1 if no timers, 0 if the top timer has expired, otherwise
+ // the time until the top timer in the timer priority queue will
+ // expire.
+ if (m_timerQueue.empty()) {
+ return -1.0;
+ }
+ if (m_timerQueue.top() <= 0.0) {
+ return 0.0;
+ }
+ return m_timerQueue.top();
+}
+
+Event::Type
+EventQueue::getRegisteredType(const String& name) const
+{
+ NameMap::const_iterator found = m_nameMap.find(name);
+ if (found != m_nameMap.end())
+ return found->second;
+
+ return Event::kUnknown;
+}
+
+void*
+EventQueue::getSystemTarget()
+{
+ // any unique arbitrary pointer will do
+ return &m_systemTarget;
+}
+
+void
+EventQueue::waitForReady() const
+{
+ double timeout = ARCH->time() + 10;
+ Lock lock(m_readyMutex);
+
+ while (!m_readyCondVar->wait()) {
+ if (ARCH->time() > timeout) {
+ throw std::runtime_error("event queue is not ready within 5 sec");
+ }
+ }
+}
+
+//
+// EventQueue::Timer
+//
+
+EventQueue::Timer::Timer(EventQueueTimer* timer, double timeout,
+ double initialTime, void* target, bool oneShot) :
+ m_timer(timer),
+ m_timeout(timeout),
+ m_target(target),
+ m_oneShot(oneShot),
+ m_time(initialTime)
+{
+ assert(m_timeout > 0.0);
+}
+
+EventQueue::Timer::~Timer()
+{
+ // do nothing
+}
+
+void
+EventQueue::Timer::reset()
+{
+ m_time = m_timeout;
+}
+
+EventQueue::Timer&
+EventQueue::Timer::operator-=(double dt)
+{
+ m_time -= dt;
+ return *this;
+}
+
+EventQueue::Timer::operator double() const
+{
+ return m_time;
+}
+
+bool
+EventQueue::Timer::isOneShot() const
+{
+ return m_oneShot;
+}
+
+EventQueueTimer*
+EventQueue::Timer::getTimer() const
+{
+ return m_timer;
+}
+
+void*
+EventQueue::Timer::getTarget() const
+{
+ return m_target;
+}
+
+void
+EventQueue::Timer::fillEvent(TimerEvent& event) const
+{
+ event.m_timer = m_timer;
+ event.m_count = 0;
+ if (m_time <= 0.0) {
+ event.m_count = static_cast<UInt32>((m_timeout - m_time) / m_timeout);
+ }
+}
+
+bool
+EventQueue::Timer::operator<(const Timer& t) const
+{
+ return m_time < t.m_time;
+}
diff --git a/src/lib/base/EventQueue.h b/src/lib/base/EventQueue.h
new file mode 100644
index 0000000..97e7fba
--- /dev/null
+++ b/src/lib/base/EventQueue.h
@@ -0,0 +1,200 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "mt/CondVar.h"
+#include "arch/IArchMultithread.h"
+#include "base/IEventQueue.h"
+#include "base/Event.h"
+#include "base/PriorityQueue.h"
+#include "base/Stopwatch.h"
+#include "common/stdmap.h"
+#include "common/stdset.h"
+#include "base/NonBlockingStream.h"
+
+#include <queue>
+
+class Mutex;
+
+//! Event queue
+/*!
+An event queue that implements the platform independent parts and
+delegates the platform dependent parts to a subclass.
+*/
+class EventQueue : public IEventQueue {
+public:
+ EventQueue();
+ virtual ~EventQueue();
+
+ // IEventQueue overrides
+ virtual void loop();
+ virtual void adoptBuffer(IEventQueueBuffer*);
+ virtual bool getEvent(Event& event, double timeout = -1.0);
+ virtual bool dispatchEvent(const Event& event);
+ virtual void addEvent(const Event& event);
+ virtual EventQueueTimer*
+ newTimer(double duration, void* target);
+ virtual EventQueueTimer*
+ newOneShotTimer(double duration, void* target);
+ virtual void deleteTimer(EventQueueTimer*);
+ virtual void adoptHandler(Event::Type type,
+ void* target, IEventJob* handler);
+ virtual void removeHandler(Event::Type type, void* target);
+ virtual void removeHandlers(void* target);
+ virtual Event::Type
+ registerTypeOnce(Event::Type& type, const char* name);
+ virtual bool isEmpty() const;
+ virtual IEventJob* getHandler(Event::Type type, void* target) const;
+ virtual const char* getTypeName(Event::Type type);
+ virtual Event::Type
+ getRegisteredType(const String& name) const;
+ void* getSystemTarget();
+ virtual void waitForReady() const;
+
+private:
+ UInt32 saveEvent(const Event& event);
+ Event removeEvent(UInt32 eventID);
+ bool hasTimerExpired(Event& event);
+ double getNextTimerTimeout() const;
+ void addEventToBuffer(const Event& event);
+ bool parent_requests_shutdown() const;
+
+private:
+ class Timer {
+ public:
+ Timer(EventQueueTimer*, double timeout, double initialTime,
+ void* target, bool oneShot);
+ ~Timer();
+
+ void reset();
+
+ Timer& operator-=(double);
+
+ operator double() const;
+
+ bool isOneShot() const;
+ EventQueueTimer*
+ getTimer() const;
+ void* getTarget() const;
+ void fillEvent(TimerEvent&) const;
+
+ bool operator<(const Timer&) const;
+
+ private:
+ EventQueueTimer* m_timer;
+ double m_timeout;
+ void* m_target;
+ bool m_oneShot;
+ double m_time;
+ };
+
+ typedef std::set<EventQueueTimer*> Timers;
+ typedef PriorityQueue<Timer> TimerQueue;
+ typedef std::map<UInt32, Event> EventTable;
+ typedef std::vector<UInt32> EventIDList;
+ typedef std::map<Event::Type, const char*> TypeMap;
+ typedef std::map<String, Event::Type> NameMap;
+ typedef std::map<Event::Type, IEventJob*> TypeHandlerTable;
+ typedef std::map<void*, TypeHandlerTable> HandlerTable;
+
+ int m_systemTarget;
+ ArchMutex m_mutex;
+
+ // registered events
+ Event::Type m_nextType;
+ TypeMap m_typeMap;
+ NameMap m_nameMap;
+
+ // buffer of events
+ IEventQueueBuffer* m_buffer;
+
+ // saved events
+ EventTable m_events;
+ EventIDList m_oldEventIDs;
+
+ // timers
+ Stopwatch m_time;
+ Timers m_timers;
+ TimerQueue m_timerQueue;
+ TimerEvent m_timerEvent;
+
+ // event handlers
+ HandlerTable m_handlers;
+
+public:
+ //
+ // Event type providers.
+ //
+ ClientEvents& forClient();
+ IStreamEvents& forIStream();
+ IpcClientEvents& forIpcClient();
+ IpcClientProxyEvents& forIpcClientProxy();
+ IpcServerEvents& forIpcServer();
+ IpcServerProxyEvents& forIpcServerProxy();
+ IDataSocketEvents& forIDataSocket();
+ IListenSocketEvents& forIListenSocket();
+ ISocketEvents& forISocket();
+ OSXScreenEvents& forOSXScreen();
+ ClientListenerEvents& forClientListener();
+ ClientProxyEvents& forClientProxy();
+ ClientProxyUnknownEvents& forClientProxyUnknown();
+ ServerEvents& forServer();
+ ServerAppEvents& forServerApp();
+ IKeyStateEvents& forIKeyState();
+ IPrimaryScreenEvents& forIPrimaryScreen();
+ IScreenEvents& forIScreen();
+ ClipboardEvents& forClipboard();
+ FileEvents& forFile();
+
+private:
+ ClientEvents* m_typesForClient;
+ IStreamEvents* m_typesForIStream;
+ IpcClientEvents* m_typesForIpcClient;
+ IpcClientProxyEvents* m_typesForIpcClientProxy;
+ IpcServerEvents* m_typesForIpcServer;
+ IpcServerProxyEvents* m_typesForIpcServerProxy;
+ IDataSocketEvents* m_typesForIDataSocket;
+ IListenSocketEvents* m_typesForIListenSocket;
+ ISocketEvents* m_typesForISocket;
+ OSXScreenEvents* m_typesForOSXScreen;
+ ClientListenerEvents* m_typesForClientListener;
+ ClientProxyEvents* m_typesForClientProxy;
+ ClientProxyUnknownEvents* m_typesForClientProxyUnknown;
+ ServerEvents* m_typesForServer;
+ ServerAppEvents* m_typesForServerApp;
+ IKeyStateEvents* m_typesForIKeyState;
+ IPrimaryScreenEvents* m_typesForIPrimaryScreen;
+ IScreenEvents* m_typesForIScreen;
+ ClipboardEvents* m_typesForClipboard;
+ FileEvents* m_typesForFile;
+ Mutex* m_readyMutex;
+ CondVar<bool>* m_readyCondVar;
+ std::queue<Event> m_pending;
+ NonBlockingStream m_parentStream;
+};
+
+#define EVENT_TYPE_ACCESSOR(type_) \
+type_##Events& \
+EventQueue::for##type_() { \
+ if (m_typesFor##type_ == NULL) { \
+ m_typesFor##type_ = new type_##Events(); \
+ m_typesFor##type_->setEvents(dynamic_cast<IEventQueue*>(this)); \
+ } \
+ return *m_typesFor##type_; \
+}
diff --git a/src/lib/base/EventTypes.cpp b/src/lib/base/EventTypes.cpp
new file mode 100644
index 0000000..9a3e46c
--- /dev/null
+++ b/src/lib/base/EventTypes.cpp
@@ -0,0 +1,203 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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 "base/EventTypes.h"
+#include "base/IEventQueue.h"
+
+#include <assert.h>
+#include <stddef.h>
+
+EventTypes::EventTypes() :
+ m_events(NULL)
+{
+}
+
+IEventQueue*
+EventTypes::getEvents() const
+{
+ assert(m_events != NULL);
+ return m_events;
+}
+
+void
+EventTypes::setEvents(IEventQueue* events)
+{
+ m_events = events;
+}
+
+//
+// Client
+//
+
+REGISTER_EVENT(Client, connected)
+REGISTER_EVENT(Client, connectionFailed)
+REGISTER_EVENT(Client, disconnected)
+
+//
+// IStream
+//
+
+REGISTER_EVENT(IStream, inputReady)
+REGISTER_EVENT(IStream, outputFlushed)
+REGISTER_EVENT(IStream, outputError)
+REGISTER_EVENT(IStream, inputShutdown)
+REGISTER_EVENT(IStream, outputShutdown)
+
+//
+// IpcClient
+//
+
+REGISTER_EVENT(IpcClient, connected)
+REGISTER_EVENT(IpcClient, messageReceived)
+
+//
+// IpcClientProxy
+//
+
+REGISTER_EVENT(IpcClientProxy, messageReceived)
+REGISTER_EVENT(IpcClientProxy, disconnected)
+
+//
+// IpcServerProxy
+//
+
+REGISTER_EVENT(IpcServerProxy, messageReceived)
+
+//
+// IDataSocket
+//
+
+REGISTER_EVENT(IDataSocket, connected)
+REGISTER_EVENT(IDataSocket, secureConnected)
+REGISTER_EVENT(IDataSocket, connectionFailed)
+
+//
+// IListenSocket
+//
+
+REGISTER_EVENT(IListenSocket, connecting)
+
+//
+// ISocket
+//
+
+REGISTER_EVENT(ISocket, disconnected)
+REGISTER_EVENT(ISocket, stopRetry)
+
+//
+// OSXScreen
+//
+
+REGISTER_EVENT(OSXScreen, confirmSleep)
+
+//
+// ClientListener
+//
+
+REGISTER_EVENT(ClientListener, accepted)
+REGISTER_EVENT(ClientListener, connected)
+
+//
+// ClientProxy
+//
+
+REGISTER_EVENT(ClientProxy, ready)
+REGISTER_EVENT(ClientProxy, disconnected)
+
+//
+// ClientProxyUnknown
+//
+
+REGISTER_EVENT(ClientProxyUnknown, success)
+REGISTER_EVENT(ClientProxyUnknown, failure)
+
+//
+// Server
+//
+
+REGISTER_EVENT(Server, error)
+REGISTER_EVENT(Server, connected)
+REGISTER_EVENT(Server, disconnected)
+REGISTER_EVENT(Server, switchToScreen)
+REGISTER_EVENT(Server, switchInDirection)
+REGISTER_EVENT(Server, keyboardBroadcast)
+REGISTER_EVENT(Server, lockCursorToScreen)
+REGISTER_EVENT(Server, screenSwitched)
+
+//
+// ServerApp
+//
+
+REGISTER_EVENT(ServerApp, reloadConfig)
+REGISTER_EVENT(ServerApp, forceReconnect)
+REGISTER_EVENT(ServerApp, resetServer)
+
+//
+// IKeyState
+//
+
+REGISTER_EVENT(IKeyState, keyDown)
+REGISTER_EVENT(IKeyState, keyUp)
+REGISTER_EVENT(IKeyState, keyRepeat)
+
+//
+// IPrimaryScreen
+//
+
+REGISTER_EVENT(IPrimaryScreen, buttonDown)
+REGISTER_EVENT(IPrimaryScreen, buttonUp)
+REGISTER_EVENT(IPrimaryScreen, motionOnPrimary)
+REGISTER_EVENT(IPrimaryScreen, motionOnSecondary)
+REGISTER_EVENT(IPrimaryScreen, wheel)
+REGISTER_EVENT(IPrimaryScreen, screensaverActivated)
+REGISTER_EVENT(IPrimaryScreen, screensaverDeactivated)
+REGISTER_EVENT(IPrimaryScreen, hotKeyDown)
+REGISTER_EVENT(IPrimaryScreen, hotKeyUp)
+REGISTER_EVENT(IPrimaryScreen, fakeInputBegin)
+REGISTER_EVENT(IPrimaryScreen, fakeInputEnd)
+
+//
+// IScreen
+//
+
+REGISTER_EVENT(IScreen, error)
+REGISTER_EVENT(IScreen, shapeChanged)
+REGISTER_EVENT(IScreen, suspend)
+REGISTER_EVENT(IScreen, resume)
+
+//
+// IpcServer
+//
+
+REGISTER_EVENT(IpcServer, clientConnected)
+REGISTER_EVENT(IpcServer, messageReceived)
+
+//
+// Clipboard
+//
+
+REGISTER_EVENT(Clipboard, clipboardGrabbed)
+REGISTER_EVENT(Clipboard, clipboardChanged)
+REGISTER_EVENT(Clipboard, clipboardSending)
+
+//
+// File
+//
+
+REGISTER_EVENT(File, fileChunkSending)
+REGISTER_EVENT(File, fileRecieveCompleted)
+REGISTER_EVENT(File, keepAlive)
diff --git a/src/lib/base/EventTypes.h b/src/lib/base/EventTypes.h
new file mode 100644
index 0000000..d044c86
--- /dev/null
+++ b/src/lib/base/EventTypes.h
@@ -0,0 +1,754 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#include "base/Event.h"
+
+class IEventQueue;
+
+class EventTypes {
+public:
+ EventTypes();
+ void setEvents(IEventQueue* events);
+
+protected:
+ IEventQueue* getEvents() const;
+
+private:
+ IEventQueue* m_events;
+};
+
+#define REGISTER_EVENT(type_, name_) \
+Event::Type \
+type_##Events::name_() \
+{ \
+ return getEvents()->registerTypeOnce(m_##name_, __FUNCTION__); \
+}
+
+class ClientEvents : public EventTypes {
+public:
+ ClientEvents() :
+ m_connected(Event::kUnknown),
+ m_connectionFailed(Event::kUnknown),
+ m_disconnected(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get connected event type
+ /*!
+ Returns the connected event type. This is sent when the client has
+ successfully connected to the server.
+ */
+ Event::Type connected();
+
+ //! Get connection failed event type
+ /*!
+ Returns the connection failed event type. This is sent when the
+ server fails for some reason. The event data is a FailInfo*.
+ */
+ Event::Type connectionFailed();
+
+ //! Get disconnected event type
+ /*!
+ Returns the disconnected event type. This is sent when the client
+ has disconnected from the server (and only after having successfully
+ connected).
+ */
+ Event::Type disconnected();
+
+ //@}
+
+private:
+ Event::Type m_connected;
+ Event::Type m_connectionFailed;
+ Event::Type m_disconnected;
+};
+
+class IStreamEvents : public EventTypes {
+public:
+ IStreamEvents() :
+ m_inputReady(Event::kUnknown),
+ m_outputFlushed(Event::kUnknown),
+ m_outputError(Event::kUnknown),
+ m_inputShutdown(Event::kUnknown),
+ m_outputShutdown(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get input ready event type
+ /*!
+ Returns the input ready event type. A stream sends this event
+ when \c read() will return with data.
+ */
+ Event::Type inputReady();
+
+ //! Get output flushed event type
+ /*!
+ Returns the output flushed event type. A stream sends this event
+ when the output buffer has been flushed. If there have been no
+ writes since the event was posted, calling \c shutdownOutput() or
+ \c close() will not discard any data and \c flush() will return
+ immediately.
+ */
+ Event::Type outputFlushed();
+
+ //! Get output error event type
+ /*!
+ Returns the output error event type. A stream sends this event
+ when a write has failed.
+ */
+ Event::Type outputError();
+
+ //! Get input shutdown event type
+ /*!
+ Returns the input shutdown event type. This is sent when the
+ input side of the stream has shutdown. When the input has
+ shutdown, no more data will ever be available to read.
+ */
+ Event::Type inputShutdown();
+
+ //! Get output shutdown event type
+ /*!
+ Returns the output shutdown event type. This is sent when the
+ output side of the stream has shutdown. When the output has
+ shutdown, no more data can ever be written to the stream. Any
+ attempt to do so will generate a output error event.
+ */
+ Event::Type outputShutdown();
+
+ //@}
+
+private:
+ Event::Type m_inputReady;
+ Event::Type m_outputFlushed;
+ Event::Type m_outputError;
+ Event::Type m_inputShutdown;
+ Event::Type m_outputShutdown;
+};
+
+class IpcClientEvents : public EventTypes {
+public:
+ IpcClientEvents() :
+ m_connected(Event::kUnknown),
+ m_messageReceived(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Raised when the socket is connected.
+ Event::Type connected();
+
+ //! Raised when a message is received.
+ Event::Type messageReceived();
+
+ //@}
+
+private:
+ Event::Type m_connected;
+ Event::Type m_messageReceived;
+};
+
+class IpcClientProxyEvents : public EventTypes {
+public:
+ IpcClientProxyEvents() :
+ m_messageReceived(Event::kUnknown),
+ m_disconnected(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Raised when the server receives a message from a client.
+ Event::Type messageReceived();
+
+ //! Raised when the client disconnects from the server.
+ Event::Type disconnected();
+
+ //@}
+
+private:
+ Event::Type m_messageReceived;
+ Event::Type m_disconnected;
+};
+
+class IpcServerEvents : public EventTypes {
+public:
+ IpcServerEvents() :
+ m_clientConnected(Event::kUnknown),
+ m_messageReceived(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Raised when we have created the client proxy.
+ Event::Type clientConnected();
+
+ //! Raised when a message is received through a client proxy.
+ Event::Type messageReceived();
+
+ //@}
+
+private:
+ Event::Type m_clientConnected;
+ Event::Type m_messageReceived;
+};
+
+class IpcServerProxyEvents : public EventTypes {
+public:
+ IpcServerProxyEvents() :
+ m_messageReceived(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Raised when the client receives a message from the server.
+ Event::Type messageReceived();
+
+ //@}
+
+private:
+ Event::Type m_messageReceived;
+};
+
+class IDataSocketEvents : public EventTypes {
+public:
+ IDataSocketEvents() :
+ m_connected(Event::kUnknown),
+ m_secureConnected(Event::kUnknown),
+ m_connectionFailed(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get connected event type
+ /*!
+ Returns the socket connected event type. A socket sends this
+ event when a remote connection has been established.
+ */
+ Event::Type connected();
+
+ //! Get secure connected event type
+ /*!
+ Returns the secure socket connected event type. A secure socket sends
+ this event when a remote connection has been established.
+ */
+ Event::Type secureConnected();
+
+ //! Get connection failed event type
+ /*!
+ Returns the socket connection failed event type. A socket sends
+ this event when an attempt to connect to a remote port has failed.
+ The data is a pointer to a ConnectionFailedInfo.
+ */
+ Event::Type connectionFailed();
+
+ //@}
+
+private:
+ Event::Type m_connected;
+ Event::Type m_secureConnected;
+ Event::Type m_connectionFailed;
+};
+
+class IListenSocketEvents : public EventTypes {
+public:
+ IListenSocketEvents() :
+ m_connecting(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get connecting event type
+ /*!
+ Returns the socket connecting event type. A socket sends this
+ event when a remote connection is waiting to be accepted.
+ */
+ Event::Type connecting();
+
+ //@}
+
+private:
+ Event::Type m_connecting;
+};
+
+class ISocketEvents : public EventTypes {
+public:
+ ISocketEvents() :
+ m_disconnected(Event::kUnknown),
+ m_stopRetry(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get disconnected event type
+ /*!
+ Returns the socket disconnected event type. A socket sends this
+ event when the remote side of the socket has disconnected or
+ shutdown both input and output.
+ */
+ Event::Type disconnected();
+
+ //! Get stop retry event type
+ /*!
+ Returns the stop retry event type. This is sent when the client
+ doesn't want to reconnect after it disconnects from the server.
+ */
+ Event::Type stopRetry();
+
+ //@}
+
+private:
+ Event::Type m_disconnected;
+ Event::Type m_stopRetry;
+};
+
+class OSXScreenEvents : public EventTypes {
+public:
+ OSXScreenEvents() :
+ m_confirmSleep(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ Event::Type confirmSleep();
+
+ //@}
+
+private:
+ Event::Type m_confirmSleep;
+};
+
+class ClientListenerEvents : public EventTypes {
+public:
+ ClientListenerEvents() :
+ m_accepted(Event::kUnknown),
+ m_connected(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get accepted event type
+ /*!
+ Returns the accepted event type. This is sent whenever a server
+ accepts a client.
+ */
+ Event::Type accepted();
+
+ //! Get connected event type
+ /*!
+ Returns the connected event type. This is sent whenever a
+ a client connects.
+ */
+ Event::Type connected();
+
+ //@}
+
+private:
+ Event::Type m_accepted;
+ Event::Type m_connected;
+};
+
+class ClientProxyEvents : public EventTypes {
+public:
+ ClientProxyEvents() :
+ m_ready(Event::kUnknown),
+ m_disconnected(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get ready event type
+ /*!
+ Returns the ready event type. This is sent when the client has
+ completed the initial handshake. Until it is sent, the client is
+ not fully connected.
+ */
+ Event::Type ready();
+
+ //! Get disconnect event type
+ /*!
+ Returns the disconnect event type. This is sent when the client
+ disconnects or is disconnected. The target is getEventTarget().
+ */
+ Event::Type disconnected();
+
+ //@}
+
+private:
+ Event::Type m_ready;
+ Event::Type m_disconnected;
+};
+
+class ClientProxyUnknownEvents : public EventTypes {
+public:
+ ClientProxyUnknownEvents() :
+ m_success(Event::kUnknown),
+ m_failure(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get success event type
+ /*!
+ Returns the success event type. This is sent when the client has
+ correctly responded to the hello message. The target is this.
+ */
+ Event::Type success();
+
+ //! Get failure event type
+ /*!
+ Returns the failure event type. This is sent when a client fails
+ to correctly respond to the hello message. The target is this.
+ */
+ Event::Type failure();
+
+ //@}
+
+private:
+ Event::Type m_success;
+ Event::Type m_failure;
+};
+
+class ServerEvents : public EventTypes {
+public:
+ ServerEvents() :
+ m_error(Event::kUnknown),
+ m_connected(Event::kUnknown),
+ m_disconnected(Event::kUnknown),
+ m_switchToScreen(Event::kUnknown),
+ m_switchInDirection(Event::kUnknown),
+ m_keyboardBroadcast(Event::kUnknown),
+ m_lockCursorToScreen(Event::kUnknown),
+ m_screenSwitched(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get error event type
+ /*!
+ Returns the error event type. This is sent when the server fails
+ for some reason.
+ */
+ Event::Type error();
+
+ //! Get connected event type
+ /*!
+ Returns the connected event type. This is sent when a client screen
+ has connected. The event data is a \c ScreenConnectedInfo* that
+ indicates the connected screen.
+ */
+ Event::Type connected();
+
+ //! Get disconnected event type
+ /*!
+ Returns the disconnected event type. This is sent when all the
+ clients have disconnected.
+ */
+ Event::Type disconnected();
+
+ //! Get switch to screen event type
+ /*!
+ Returns the switch to screen event type. The server responds to this
+ by switching screens. The event data is a \c SwitchToScreenInfo*
+ that indicates the target screen.
+ */
+ Event::Type switchToScreen();
+
+ //! Get switch in direction event type
+ /*!
+ Returns the switch in direction event type. The server responds to this
+ by switching screens. The event data is a \c SwitchInDirectionInfo*
+ that indicates the target direction.
+ */
+ Event::Type switchInDirection();
+
+ //! Get keyboard broadcast event type
+ /*!
+ Returns the keyboard broadcast event type. The server responds
+ to this by turning on keyboard broadcasting or turning it off. The
+ event data is a \c KeyboardBroadcastInfo*.
+ */
+ Event::Type keyboardBroadcast();
+
+ //! Get lock cursor event type
+ /*!
+ Returns the lock cursor event type. The server responds to this
+ by locking the cursor to the active screen or unlocking it. The
+ event data is a \c LockCursorToScreenInfo*.
+ */
+ Event::Type lockCursorToScreen();
+
+ //! Get screen switched event type
+ /*!
+ Returns the screen switched event type. This is raised when the
+ screen has been switched to a client.
+ */
+ Event::Type screenSwitched();
+
+ //@}
+
+private:
+ Event::Type m_error;
+ Event::Type m_connected;
+ Event::Type m_disconnected;
+ Event::Type m_switchToScreen;
+ Event::Type m_switchInDirection;
+ Event::Type m_keyboardBroadcast;
+ Event::Type m_lockCursorToScreen;
+ Event::Type m_screenSwitched;
+};
+
+class ServerAppEvents : public EventTypes {
+public:
+ ServerAppEvents() :
+ m_reloadConfig(Event::kUnknown),
+ m_forceReconnect(Event::kUnknown),
+ m_resetServer(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ Event::Type reloadConfig();
+ Event::Type forceReconnect();
+ Event::Type resetServer();
+
+ //@}
+
+private:
+ Event::Type m_reloadConfig;
+ Event::Type m_forceReconnect;
+ Event::Type m_resetServer;
+};
+
+class IKeyStateEvents : public EventTypes {
+public:
+ IKeyStateEvents() :
+ m_keyDown(Event::kUnknown),
+ m_keyUp(Event::kUnknown),
+ m_keyRepeat(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get key down event type. Event data is KeyInfo*, count == 1.
+ Event::Type keyDown();
+
+ //! Get key up event type. Event data is KeyInfo*, count == 1.
+ Event::Type keyUp();
+
+ //! Get key repeat event type. Event data is KeyInfo*.
+ Event::Type keyRepeat();
+
+ //@}
+
+private:
+ Event::Type m_keyDown;
+ Event::Type m_keyUp;
+ Event::Type m_keyRepeat;
+};
+
+class IPrimaryScreenEvents : public EventTypes {
+public:
+ IPrimaryScreenEvents() :
+ m_buttonDown(Event::kUnknown),
+ m_buttonUp(Event::kUnknown),
+ m_motionOnPrimary(Event::kUnknown),
+ m_motionOnSecondary(Event::kUnknown),
+ m_wheel(Event::kUnknown),
+ m_screensaverActivated(Event::kUnknown),
+ m_screensaverDeactivated(Event::kUnknown),
+ m_hotKeyDown(Event::kUnknown),
+ m_hotKeyUp(Event::kUnknown),
+ m_fakeInputBegin(Event::kUnknown),
+ m_fakeInputEnd(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! button down event type. Event data is ButtonInfo*.
+ Event::Type buttonDown();
+
+ //! button up event type. Event data is ButtonInfo*.
+ Event::Type buttonUp();
+
+ //! mouse motion on the primary screen event type
+ /*!
+ Event data is MotionInfo* and the values are an absolute position.
+ */
+ Event::Type motionOnPrimary();
+
+ //! mouse motion on a secondary screen event type
+ /*!
+ Event data is MotionInfo* and the values are motion deltas not
+ absolute coordinates.
+ */
+ Event::Type motionOnSecondary();
+
+ //! mouse wheel event type. Event data is WheelInfo*.
+ Event::Type wheel();
+
+ //! screensaver activated event type
+ Event::Type screensaverActivated();
+
+ //! screensaver deactivated event type
+ Event::Type screensaverDeactivated();
+
+ //! hot key down event type. Event data is HotKeyInfo*.
+ Event::Type hotKeyDown();
+
+ //! hot key up event type. Event data is HotKeyInfo*.
+ Event::Type hotKeyUp();
+
+ //! start of fake input event type
+ Event::Type fakeInputBegin();
+
+ //! end of fake input event type
+ Event::Type fakeInputEnd();
+
+ //@}
+
+private:
+ Event::Type m_buttonDown;
+ Event::Type m_buttonUp;
+ Event::Type m_motionOnPrimary;
+ Event::Type m_motionOnSecondary;
+ Event::Type m_wheel;
+ Event::Type m_screensaverActivated;
+ Event::Type m_screensaverDeactivated;
+ Event::Type m_hotKeyDown;
+ Event::Type m_hotKeyUp;
+ Event::Type m_fakeInputBegin;
+ Event::Type m_fakeInputEnd;
+};
+
+class IScreenEvents : public EventTypes {
+public:
+ IScreenEvents() :
+ m_error(Event::kUnknown),
+ m_shapeChanged(Event::kUnknown),
+ m_suspend(Event::kUnknown),
+ m_resume(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get error event type
+ /*!
+ Returns the error event type. This is sent whenever the screen has
+ failed for some reason (e.g. the X Windows server died).
+ */
+ Event::Type error();
+
+ //! Get shape changed event type
+ /*!
+ Returns the shape changed event type. This is sent whenever the
+ screen's shape changes.
+ */
+ Event::Type shapeChanged();
+
+ //! Get suspend event type
+ /*!
+ Returns the suspend event type. This is sent whenever the system goes
+ to sleep or a user session is deactivated (fast user switching).
+ */
+ Event::Type suspend();
+
+ //! Get resume event type
+ /*!
+ Returns the resume event type. This is sent whenever the system wakes
+ up or a user session is activated (fast user switching).
+ */
+ Event::Type resume();
+
+ //@}
+
+private:
+ Event::Type m_error;
+ Event::Type m_shapeChanged;
+ Event::Type m_suspend;
+ Event::Type m_resume;
+};
+
+class ClipboardEvents : public EventTypes {
+public:
+ ClipboardEvents() :
+ m_clipboardGrabbed(Event::kUnknown),
+ m_clipboardChanged(Event::kUnknown),
+ m_clipboardSending(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Get clipboard grabbed event type
+ /*!
+ Returns the clipboard grabbed event type. This is sent whenever the
+ clipboard is grabbed by some other application so we don't own it
+ anymore. The data is a pointer to a ClipboardInfo.
+ */
+ Event::Type clipboardGrabbed();
+
+ //! Get clipboard changed event type
+ /*!
+ Returns the clipboard changed event type. This is sent whenever the
+ contents of the clipboard has changed. The data is a pointer to a
+ IScreen::ClipboardInfo.
+ */
+ Event::Type clipboardChanged();
+
+ //! Clipboard sending event type
+ /*!
+ Returns the clipboard sending event type. This is used to send
+ clipboard chunks.
+ */
+ Event::Type clipboardSending();
+
+ //@}
+
+private:
+ Event::Type m_clipboardGrabbed;
+ Event::Type m_clipboardChanged;
+ Event::Type m_clipboardSending;
+};
+
+class FileEvents : public EventTypes {
+public:
+ FileEvents() :
+ m_fileChunkSending(Event::kUnknown),
+ m_fileRecieveCompleted(Event::kUnknown),
+ m_keepAlive(Event::kUnknown) { }
+
+ //! @name accessors
+ //@{
+
+ //! Sending a file chunk
+ Event::Type fileChunkSending();
+
+ //! Completed receiving a file
+ Event::Type fileRecieveCompleted();
+
+ //! Send a keep alive
+ Event::Type keepAlive();
+
+ //@}
+
+private:
+ Event::Type m_fileChunkSending;
+ Event::Type m_fileRecieveCompleted;
+ Event::Type m_keepAlive;
+};
diff --git a/src/lib/base/FunctionEventJob.cpp b/src/lib/base/FunctionEventJob.cpp
new file mode 100644
index 0000000..705e058
--- /dev/null
+++ b/src/lib/base/FunctionEventJob.cpp
@@ -0,0 +1,44 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "base/FunctionEventJob.h"
+
+//
+// FunctionEventJob
+//
+
+FunctionEventJob::FunctionEventJob(
+ void (*func)(const Event&, void*), void* arg) :
+ m_func(func),
+ m_arg(arg)
+{
+ // do nothing
+}
+
+FunctionEventJob::~FunctionEventJob()
+{
+ // do nothing
+}
+
+void
+FunctionEventJob::run(const Event& event)
+{
+ if (m_func != NULL) {
+ m_func(event, m_arg);
+ }
+}
diff --git a/src/lib/base/FunctionEventJob.h b/src/lib/base/FunctionEventJob.h
new file mode 100644
index 0000000..4b2c2fc
--- /dev/null
+++ b/src/lib/base/FunctionEventJob.h
@@ -0,0 +1,39 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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/IEventJob.h"
+
+//! Use a function as an event job
+/*!
+An event job class that invokes a function.
+*/
+class FunctionEventJob : public IEventJob {
+public:
+ //! run() invokes \c func(arg)
+ FunctionEventJob(void (*func)(const Event&, void*), void* arg = NULL);
+ virtual ~FunctionEventJob();
+
+ // IEventJob overrides
+ virtual void run(const Event&);
+
+private:
+ void (*m_func)(const Event&, void*);
+ void* m_arg;
+};
diff --git a/src/lib/base/FunctionJob.cpp b/src/lib/base/FunctionJob.cpp
new file mode 100644
index 0000000..859010e
--- /dev/null
+++ b/src/lib/base/FunctionJob.cpp
@@ -0,0 +1,43 @@
+/*
+ * barrier -- 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 "base/FunctionJob.h"
+
+//
+// FunctionJob
+//
+
+FunctionJob::FunctionJob(void (*func)(void*), void* arg) :
+ m_func(func),
+ m_arg(arg)
+{
+ // do nothing
+}
+
+FunctionJob::~FunctionJob()
+{
+ // do nothing
+}
+
+void
+FunctionJob::run()
+{
+ if (m_func != NULL) {
+ m_func(m_arg);
+ }
+}
diff --git a/src/lib/base/FunctionJob.h b/src/lib/base/FunctionJob.h
new file mode 100644
index 0000000..9cdfa9d
--- /dev/null
+++ b/src/lib/base/FunctionJob.h
@@ -0,0 +1,39 @@
+/*
+ * barrier -- 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/IJob.h"
+
+//! Use a function as a job
+/*!
+A job class that invokes a function.
+*/
+class FunctionJob : public IJob {
+public:
+ //! run() invokes \c func(arg)
+ FunctionJob(void (*func)(void*), void* arg = NULL);
+ virtual ~FunctionJob();
+
+ // IJob overrides
+ virtual void run();
+
+private:
+ void (*m_func)(void*);
+ void* m_arg;
+};
diff --git a/src/lib/base/IEventJob.h b/src/lib/base/IEventJob.h
new file mode 100644
index 0000000..3e4a420
--- /dev/null
+++ b/src/lib/base/IEventJob.h
@@ -0,0 +1,33 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "common/IInterface.h"
+
+class Event;
+
+//! Event handler interface
+/*!
+An event job is an interface for executing a event handler.
+*/
+class IEventJob : public IInterface {
+public:
+ //! Run the job
+ virtual void run(const Event&) = 0;
+};
diff --git a/src/lib/base/IEventQueue.h b/src/lib/base/IEventQueue.h
new file mode 100644
index 0000000..cd4f0b3
--- /dev/null
+++ b/src/lib/base/IEventQueue.h
@@ -0,0 +1,251 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "common/IInterface.h"
+#include "base/Event.h"
+#include "base/String.h"
+
+class IEventJob;
+class IEventQueueBuffer;
+
+// Opaque type for timer info. This is defined by subclasses of
+// IEventQueueBuffer.
+class EventQueueTimer;
+
+// Event type registration classes.
+class ClientEvents;
+class IStreamEvents;
+class IpcClientEvents;
+class IpcClientProxyEvents;
+class IpcServerEvents;
+class IpcServerProxyEvents;
+class IDataSocketEvents;
+class IListenSocketEvents;
+class ISocketEvents;
+class OSXScreenEvents;
+class ClientListenerEvents;
+class ClientProxyEvents;
+class ClientProxyUnknownEvents;
+class ServerEvents;
+class ServerAppEvents;
+class IKeyStateEvents;
+class IPrimaryScreenEvents;
+class IScreenEvents;
+class ClipboardEvents;
+class FileEvents;
+
+//! Event queue interface
+/*!
+An event queue provides a queue of Events. Clients can block waiting
+on any event becoming available at the head of the queue and can place
+new events at the end of the queue. Clients can also add and remove
+timers which generate events periodically.
+*/
+class IEventQueue : public IInterface {
+public:
+ class TimerEvent {
+ public:
+ EventQueueTimer* m_timer; //!< The timer
+ UInt32 m_count; //!< Number of repeats
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! Loop the event queue until quit
+ /*!
+ Dequeues and dispatches events until the kQuit event is found.
+ */
+ virtual void loop() = 0;
+
+ //! Set the buffer
+ /*!
+ Replace the current event queue buffer. Any queued events are
+ discarded. The queue takes ownership of the buffer.
+ */
+ virtual void adoptBuffer(IEventQueueBuffer*) = 0;
+
+ //! Remove event from queue
+ /*!
+ Returns the next event on the queue into \p event. If no event is
+ available then blocks for up to \p timeout seconds, or forever if
+ \p timeout is negative. Returns true iff an event was available.
+ */
+ virtual bool getEvent(Event& event, double timeout = -1.0) = 0;
+
+ //! Dispatch an event
+ /*!
+ Looks up the dispatcher for the event's target and invokes it.
+ Returns true iff a dispatcher exists for the target.
+ */
+ virtual bool dispatchEvent(const Event& event) = 0;
+
+ //! Add event to queue
+ /*!
+ Adds \p event to the end of the queue.
+ */
+ virtual void addEvent(const Event& event) = 0;
+
+ //! Create a recurring timer
+ /*!
+ Creates and returns a timer. An event is returned after \p duration
+ seconds and the timer is reset to countdown again. When a timer event
+ is returned the data points to a \c TimerEvent. The client must pass
+ the returned timer to \c deleteTimer() (whether or not the timer has
+ expired) to release the timer. The returned timer event uses the
+ given \p target. If \p target is NULL it uses the returned timer as
+ the target.
+
+ Events for a single timer don't accumulate in the queue, even if the
+ client reading events can't keep up. Instead, the \c m_count member
+ of the \c TimerEvent indicates how many events for the timer would
+ have been put on the queue since the last event for the timer was
+ removed (or since the timer was added).
+ */
+ virtual EventQueueTimer*
+ newTimer(double duration, void* target) = 0;
+
+ //! Create a one-shot timer
+ /*!
+ Creates and returns a one-shot timer. An event is returned when
+ the timer expires and the timer is removed from further handling.
+ When a timer event is returned the data points to a \c TimerEvent.
+ The c_count member of the \c TimerEvent is always 1. The client
+ must pass the returned timer to \c deleteTimer() (whether or not the
+ timer has expired) to release the timer. The returned timer event
+ uses the given \p target. If \p target is NULL it uses the returned
+ timer as the target.
+ */
+ virtual EventQueueTimer*
+ newOneShotTimer(double duration,
+ void* target) = 0;
+
+ //! Destroy a timer
+ /*!
+ Destroys a previously created timer. The timer is removed from the
+ queue and will not generate event, even if the timer has expired.
+ */
+ virtual void deleteTimer(EventQueueTimer*) = 0;
+
+ //! Register an event handler for an event type
+ /*!
+ Registers an event handler for \p type and \p target. The \p handler
+ is adopted. Any existing handler for the type,target pair is deleted.
+ \c dispatchEvent() will invoke \p handler for any event for \p target
+ of type \p type. If no such handler exists it will use the handler
+ for \p target and type \p kUnknown if it exists.
+ */
+ virtual void adoptHandler(Event::Type type,
+ void* target, IEventJob* handler) = 0;
+
+ //! Unregister an event handler for an event type
+ /*!
+ Unregisters an event handler for the \p type, \p target pair and
+ deletes it.
+ */
+ virtual void removeHandler(Event::Type type, void* target) = 0;
+
+ //! Unregister all event handlers for an event target
+ /*!
+ Unregisters all event handlers for the \p target and deletes them.
+ */
+ virtual void removeHandlers(void* target) = 0;
+
+ //! Creates a new event type
+ /*!
+ If \p type contains \c kUnknown then it is set to a unique event
+ type id otherwise it is left alone. The final value of \p type
+ is returned.
+ */
+ virtual Event::Type
+ registerTypeOnce(Event::Type& type,
+ const char* name) = 0;
+
+ //! Wait for event queue to become ready
+ /*!
+ Blocks on the current thread until the event queue is ready for events to
+ be added.
+ */
+ virtual void waitForReady() const = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test if queue is empty
+ /*!
+ Returns true iff the queue has no events in it, including timer
+ events.
+ */
+ virtual bool isEmpty() const = 0;
+
+ //! Get an event handler
+ /*!
+ Finds and returns the event handler for the \p type, \p target pair
+ if it exists, otherwise it returns NULL.
+ */
+ virtual IEventJob* getHandler(Event::Type type, void* target) const = 0;
+
+ //! Get name for event
+ /*!
+ Returns the name for the event \p type. This is primarily for
+ debugging.
+ */
+ virtual const char* getTypeName(Event::Type type) = 0;
+
+ //! Get an event type by name
+ /*!
+ Returns the registered type for an event for a given name.
+ */
+ virtual Event::Type getRegisteredType(const String& name) const = 0;
+
+ //! Get the system event type target
+ /*!
+ Returns the target to use for dispatching \c Event::kSystem events.
+ */
+ virtual void* getSystemTarget() = 0;
+
+ //@}
+
+ //
+ // Event type providers.
+ //
+
+ virtual ClientEvents& forClient() = 0;
+ virtual IStreamEvents& forIStream() = 0;
+ virtual IpcClientEvents& forIpcClient() = 0;
+ virtual IpcClientProxyEvents& forIpcClientProxy() = 0;
+ virtual IpcServerEvents& forIpcServer() = 0;
+ virtual IpcServerProxyEvents& forIpcServerProxy() = 0;
+ virtual IDataSocketEvents& forIDataSocket() = 0;
+ virtual IListenSocketEvents& forIListenSocket() = 0;
+ virtual ISocketEvents& forISocket() = 0;
+ virtual OSXScreenEvents& forOSXScreen() = 0;
+ virtual ClientListenerEvents& forClientListener() = 0;
+ virtual ClientProxyEvents& forClientProxy() = 0;
+ virtual ClientProxyUnknownEvents& forClientProxyUnknown() = 0;
+ virtual ServerEvents& forServer() = 0;
+ virtual ServerAppEvents& forServerApp() = 0;
+ virtual IKeyStateEvents& forIKeyState() = 0;
+ virtual IPrimaryScreenEvents& forIPrimaryScreen() = 0;
+ virtual IScreenEvents& forIScreen() = 0;
+ virtual ClipboardEvents& forClipboard() = 0;
+ virtual FileEvents& forFile() = 0;
+};
diff --git a/src/lib/base/IEventQueueBuffer.h b/src/lib/base/IEventQueueBuffer.h
new file mode 100644
index 0000000..b594436
--- /dev/null
+++ b/src/lib/base/IEventQueueBuffer.h
@@ -0,0 +1,101 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "common/IInterface.h"
+#include "common/basic_types.h"
+
+class Event;
+class EventQueueTimer;
+
+//! Event queue buffer interface
+/*!
+An event queue buffer provides a queue of events for an IEventQueue.
+*/
+class IEventQueueBuffer : public IInterface {
+public:
+ enum Type {
+ kNone, //!< No event is available
+ kSystem, //!< Event is a system event
+ kUser //!< Event is a user event
+ };
+
+ //! @name manipulators
+ //@{
+
+ //! Initialize
+ /*!
+ Useful for platform-specific initialisation from a specific thread.
+ */
+ virtual void init() = 0;
+
+ //! Block waiting for an event
+ /*!
+ Wait for an event in the event queue buffer for up to \p timeout
+ seconds.
+ */
+ virtual void waitForEvent(double timeout) = 0;
+
+ //! Get the next event
+ /*!
+ Get the next event from the buffer. Return kNone if no event is
+ available. If a system event is next, return kSystem and fill in
+ event. The event data in a system event can point to a static
+ buffer (because Event::deleteData() will not attempt to delete
+ data in a kSystem event). Otherwise, return kUser and fill in
+ \p dataID with the value passed to \c addEvent().
+ */
+ virtual Type getEvent(Event& event, UInt32& dataID) = 0;
+
+ //! Post an event
+ /*!
+ Add the given event to the end of the queue buffer. This is a user
+ event and \c getEvent() must be able to identify it as such and
+ return \p dataID. This method must cause \c waitForEvent() to
+ return at some future time if it's blocked waiting on an event.
+ */
+ virtual bool addEvent(UInt32 dataID) = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Check if event queue buffer is empty
+ /*!
+ Return true iff the event queue buffer is empty.
+ */
+ virtual bool isEmpty() const = 0;
+
+ //! Create a timer object
+ /*!
+ Create and return a timer object. The object is opaque and is
+ used only by the buffer but it must be a valid object (i.e.
+ not NULL).
+ */
+ virtual EventQueueTimer*
+ newTimer(double duration, bool oneShot) const = 0;
+
+ //! Destroy a timer object
+ /*!
+ Destroy a timer object previously returned by \c newTimer().
+ */
+ virtual void deleteTimer(EventQueueTimer*) const = 0;
+
+ //@}
+};
diff --git a/src/lib/base/IJob.h b/src/lib/base/IJob.h
new file mode 100644
index 0000000..f966ec0
--- /dev/null
+++ b/src/lib/base/IJob.h
@@ -0,0 +1,31 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+
+//! Job interface
+/*!
+A job is an interface for executing some function.
+*/
+class IJob : public IInterface {
+public:
+ //! Run the job
+ virtual void run() = 0;
+};
diff --git a/src/lib/base/ILogOutputter.h b/src/lib/base/ILogOutputter.h
new file mode 100644
index 0000000..ab218fc
--- /dev/null
+++ b/src/lib/base/ILogOutputter.h
@@ -0,0 +1,69 @@
+/*
+ * barrier -- 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/Log.h"
+#include "base/ELevel.h"
+#include "common/IInterface.h"
+
+//! Outputter interface
+/*!
+Type of outputter interface. The logger performs all output through
+outputters. ILogOutputter overrides must not call any log functions
+directly or indirectly.
+*/
+class ILogOutputter : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Open the outputter
+ /*!
+ Opens the outputter for writing. Calling this method on an
+ already open outputter must have no effect.
+ */
+ virtual void open(const char* title) = 0;
+
+ //! Close the outputter
+ /*!
+ Close the outputter. Calling this method on an already closed
+ outputter must have no effect.
+ */
+ virtual void close() = 0;
+
+ //! Show the outputter
+ /*!
+ Causes the output to become visible. This generally only makes sense
+ for a logger in a graphical user interface. Other implementations
+ will do nothing. Iff \p showIfEmpty is \c false then the implementation
+ may optionally only show the log if it's not empty.
+ */
+ virtual void show(bool showIfEmpty) = 0;
+
+ //! Write a message with level
+ /*!
+ Writes \c message, which has the given \c level, to a log.
+ If this method returns true then Log will stop passing the
+ message to all outputters in the outputter chain, otherwise
+ it continues. Most implementations should return true.
+ */
+ virtual bool write(ELevel level, const char* message) = 0;
+
+ //@}
+};
diff --git a/src/lib/base/Log.cpp b/src/lib/base/Log.cpp
new file mode 100644
index 0000000..823bf6d
--- /dev/null
+++ b/src/lib/base/Log.cpp
@@ -0,0 +1,309 @@
+/*
+ * barrier -- 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 "arch/Arch.h"
+#include "arch/XArch.h"
+#include "base/Log.h"
+#include "base/String.h"
+#include "base/log_outputters.h"
+#include "common/Version.h"
+
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+#include <ctime>
+
+// names of priorities
+static const char* g_priority[] = {
+ "FATAL",
+ "ERROR",
+ "WARNING",
+ "NOTE",
+ "INFO",
+ "DEBUG",
+ "DEBUG1",
+ "DEBUG2",
+ "DEBUG3",
+ "DEBUG4",
+ "DEBUG5"
+};
+
+// number of priorities
+static const int g_numPriority = (int)(sizeof(g_priority) / sizeof(g_priority[0]));
+
+// the default priority
+#ifndef NDEBUG
+static const int g_defaultMaxPriority = kDEBUG;
+#else
+static const int g_defaultMaxPriority = kINFO;
+#endif
+
+//
+// Log
+//
+
+Log* Log::s_log = NULL;
+
+Log::Log()
+{
+ assert(s_log == NULL);
+
+ // create mutex for multithread safe operation
+ m_mutex = ARCH->newMutex();
+
+ // other initalization
+ m_maxPriority = g_defaultMaxPriority;
+ m_maxNewlineLength = 0;
+ insert(new ConsoleLogOutputter);
+
+ s_log = this;
+}
+
+Log::Log(Log* src)
+{
+ s_log = src;
+}
+
+Log::~Log()
+{
+ // clean up
+ for (OutputterList::iterator index = m_outputters.begin();
+ index != m_outputters.end(); ++index) {
+ delete *index;
+ }
+ for (OutputterList::iterator index = m_alwaysOutputters.begin();
+ index != m_alwaysOutputters.end(); ++index) {
+ delete *index;
+ }
+ ARCH->closeMutex(m_mutex);
+}
+
+Log*
+Log::getInstance()
+{
+ assert(s_log != NULL);
+ return s_log;
+}
+
+const char*
+Log::getFilterName() const
+{
+ return getFilterName(getFilter());
+}
+
+const char*
+Log::getFilterName(int level) const
+{
+ if (level < 0) {
+ return "Message";
+ }
+ return g_priority[level];
+}
+
+void
+Log::print(const char* file, int line, const char* fmt, ...)
+{
+ // check if fmt begins with a priority argument
+ ELevel priority = kINFO;
+ if ((strlen(fmt) > 2) && (fmt[0] == '%' && fmt[1] == 'z')) {
+
+ // 060 in octal is 0 (48 in decimal), so subtracting this converts ascii
+ // number it a true number. we could use atoi instead, but this is how
+ // it was done originally.
+ priority = (ELevel)(fmt[2] - '\060');
+
+ // move the pointer on past the debug priority char
+ fmt += 3;
+ }
+
+ // done if below priority threshold
+ if (priority > getFilter()) {
+ return;
+ }
+
+ // compute prefix padding length
+ char stack[1024];
+
+ // compute suffix padding length
+ int sPad = m_maxNewlineLength;
+
+ // print to buffer, leaving space for a newline at the end and prefix
+ // at the beginning.
+ char* buffer = stack;
+ int len = (int)(sizeof(stack) / sizeof(stack[0]));
+ while (true) {
+ // try printing into the buffer
+ va_list args;
+ va_start(args, fmt);
+ int n = ARCH->vsnprintf(buffer, len - sPad, fmt, args);
+ va_end(args);
+
+ // if the buffer wasn't big enough then make it bigger and try again
+ if (n < 0 || n > (int)len) {
+ if (buffer != stack) {
+ delete[] buffer;
+ }
+ len *= 2;
+ buffer = new char[len];
+ }
+
+ // if the buffer was big enough then continue
+ else {
+ break;
+ }
+ }
+
+ // print the prefix to the buffer. leave space for priority label.
+ // do not prefix time and file for kPRINT (CLOG_PRINT)
+ if (priority != kPRINT) {
+
+ struct tm *tm;
+ char timestamp[50];
+ time_t t;
+ time(&t);
+ tm = localtime(&t);
+ sprintf(timestamp, "%04i-%02i-%02iT%02i:%02i:%02i", tm->tm_year + 1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ // square brackets, spaces, comma and null terminator take about 10
+ size_t size = 10;
+ size += strlen(timestamp);
+ size += strlen(g_priority[priority]);
+ size += strlen(buffer);
+#ifndef NDEBUG
+ size += strlen(file);
+ // assume there is no file contains over 100k lines of code
+ size += 6;
+#endif
+ char* message = new char[size];
+
+#ifndef NDEBUG
+ sprintf(message, "[%s] %s: %s\n\t%s,%d", timestamp, g_priority[priority], buffer, file, line);
+#else
+ sprintf(message, "[%s] %s: %s", timestamp, g_priority[priority], buffer);
+#endif
+
+ output(priority, message);
+ delete[] message;
+ } else {
+ output(priority, buffer);
+ }
+
+ // clean up
+ if (buffer != stack) {
+ delete[] buffer;
+ }
+}
+
+void
+Log::insert(ILogOutputter* outputter, bool alwaysAtHead)
+{
+ assert(outputter != NULL);
+
+ ArchMutexLock lock(m_mutex);
+ if (alwaysAtHead) {
+ m_alwaysOutputters.push_front(outputter);
+ }
+ else {
+ m_outputters.push_front(outputter);
+ }
+
+ outputter->open(kAppVersion);
+
+ // Issue 41
+ // don't show log unless user requests it, as some users find this
+ // feature irritating (i.e. when they lose network connectivity).
+ // in windows the log window can be displayed by selecting "show log"
+ // from the barrier system tray icon.
+ // if this causes problems for other architectures, then a different
+ // work around should be attempted.
+ //outputter->show(false);
+}
+
+void
+Log::remove(ILogOutputter* outputter)
+{
+ ArchMutexLock lock(m_mutex);
+ m_outputters.remove(outputter);
+ m_alwaysOutputters.remove(outputter);
+}
+
+void
+Log::pop_front(bool alwaysAtHead)
+{
+ ArchMutexLock lock(m_mutex);
+ OutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters;
+ if (!list->empty()) {
+ delete list->front();
+ list->pop_front();
+ }
+}
+
+bool
+Log::setFilter(const char* maxPriority)
+{
+ if (maxPriority != NULL) {
+ for (int i = 0; i < g_numPriority; ++i) {
+ if (strcmp(maxPriority, g_priority[i]) == 0) {
+ setFilter(i);
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+}
+
+void
+Log::setFilter(int maxPriority)
+{
+ ArchMutexLock lock(m_mutex);
+ m_maxPriority = maxPriority;
+}
+
+int
+Log::getFilter() const
+{
+ ArchMutexLock lock(m_mutex);
+ return m_maxPriority;
+}
+
+void
+Log::output(ELevel priority, char* msg)
+{
+ assert(priority >= -1 && priority < g_numPriority);
+ assert(msg != NULL);
+ if (!msg) return;
+
+ ArchMutexLock lock(m_mutex);
+
+ OutputterList::const_iterator i;
+
+ for (i = m_alwaysOutputters.begin(); i != m_alwaysOutputters.end(); ++i) {
+
+ // write to outputter
+ (*i)->write(priority, msg);
+ }
+
+ for (i = m_outputters.begin(); i != m_outputters.end(); ++i) {
+
+ // write to outputter and break out of loop if it returns false
+ if (!(*i)->write(priority, msg)) {
+ break;
+ }
+ }
+}
diff --git a/src/lib/base/Log.h b/src/lib/base/Log.h
new file mode 100644
index 0000000..1d09be2
--- /dev/null
+++ b/src/lib/base/Log.h
@@ -0,0 +1,211 @@
+/*
+ * barrier -- 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 "arch/IArchMultithread.h"
+#include "arch/Arch.h"
+#include "common/common.h"
+#include "common/stdlist.h"
+
+#include <stdarg.h>
+
+#define CLOG (Log::getInstance())
+#define BYE "\nTry `%s --help' for more information."
+
+class ILogOutputter;
+class Thread;
+
+//! Logging facility
+/*!
+The logging class; all console output should go through this class.
+It supports multithread safe operation, several message priority levels,
+filtering by priority, and output redirection. The macros LOG() and
+LOGC() provide convenient access.
+*/
+class Log {
+public:
+ Log();
+ Log(Log* src);
+ ~Log();
+
+ //! @name manipulators
+ //@{
+
+ //! Add an outputter to the head of the list
+ /*!
+ Inserts an outputter to the head of the outputter list. When the
+ logger writes a message, it goes to the outputter at the head of
+ the outputter list. If that outputter's \c write() method returns
+ true then it also goes to the next outputter, as so on until an
+ outputter returns false or there are no more outputters. Outputters
+ still in the outputter list when the log is destroyed will be
+ deleted. If \c alwaysAtHead is true then the outputter is always
+ called before all outputters with \c alwaysAtHead false and the
+ return value of the outputter is ignored.
+
+ By default, the logger has one outputter installed which writes to
+ the console.
+ */
+ void insert(ILogOutputter* adopted,
+ bool alwaysAtHead = false);
+
+ //! Remove an outputter from the list
+ /*!
+ Removes the first occurrence of the given outputter from the
+ outputter list. It does nothing if the outputter is not in the
+ list. The outputter is not deleted.
+ */
+ void remove(ILogOutputter* orphaned);
+
+ //! Remove the outputter from the head of the list
+ /*!
+ Removes and deletes the outputter at the head of the outputter list.
+ This does nothing if the outputter list is empty. Only removes
+ outputters that were inserted with the matching \c alwaysAtHead.
+ */
+ void pop_front(bool alwaysAtHead = false);
+
+ //! Set the minimum priority filter.
+ /*!
+ Set the filter. Messages below this priority are discarded.
+ The default priority is 4 (INFO) (unless built without NDEBUG
+ in which case it's 5 (DEBUG)). setFilter(const char*) returns
+ true if the priority \c name was recognized; if \c name is NULL
+ then it simply returns true.
+ */
+ bool setFilter(const char* name);
+
+ //! Set the minimum priority filter (by ordinal).
+ void setFilter(int);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Print a log message
+ /*!
+ Print a log message using the printf-like \c format and arguments
+ preceded by the filename and line number. If \c file is NULL then
+ neither the file nor the line are printed.
+ */
+ void print(const char* file, int line,
+ const char* format, ...);
+
+ //! Get the minimum priority level.
+ int getFilter() const;
+
+ //! Get the filter name of the current filter level.
+ const char* getFilterName() const;
+
+ //! Get the filter name of a specified filter level.
+ const char* getFilterName(int level) const;
+
+ //! Get the singleton instance of the log
+ static Log* getInstance();
+
+ //! Get the console filter level (messages above this are not sent to console).
+ int getConsoleMaxLevel() const { return kDEBUG2; }
+
+ //@}
+
+private:
+ void output(ELevel priority, char* msg);
+
+private:
+ typedef std::list<ILogOutputter*> OutputterList;
+
+ static Log* s_log;
+
+ ArchMutex m_mutex;
+ OutputterList m_outputters;
+ OutputterList m_alwaysOutputters;
+ int m_maxNewlineLength;
+ int m_maxPriority;
+};
+
+/*!
+\def LOG(arg)
+Write to the log. Because macros cannot accept variable arguments, this
+should be invoked like so:
+\code
+LOG((CLOG_XXX "%d and %d are %s", x, y, x == y ? "equal" : "not equal"));
+\endcode
+In particular, notice the double open and close parentheses. Also note
+that there is no comma after the \c CLOG_XXX. The \c XXX should be
+replaced by one of enumerants in \c Log::ELevel without the leading
+\c k. For example, \c CLOG_INFO. The special \c CLOG_PRINT level will
+not be filtered and is never prefixed by the filename and line number.
+
+If \c NOLOGGING is defined during the build then this macro expands to
+nothing. If \c NDEBUG is defined during the build then it expands to a
+call to Log::print. Otherwise it expands to a call to Log::printt,
+which includes the filename and line number.
+*/
+
+/*!
+\def LOGC(expr, arg)
+Write to the log if and only if expr is true. Because macros cannot accept
+variable arguments, this should be invoked like so:
+\code
+LOGC(x == y, (CLOG_XXX "%d and %d are equal", x, y));
+\endcode
+In particular, notice the parentheses around everything after the boolean
+expression. Also note that there is no comma after the \c CLOG_XXX.
+The \c XXX should be replaced by one of enumerants in \c Log::ELevel
+without the leading \c k. For example, \c CLOG_INFO. The special
+\c CLOG_PRINT level will not be filtered and is never prefixed by the
+filename and line number.
+
+If \c NOLOGGING is defined during the build then this macro expands to
+nothing. If \c NDEBUG is not defined during the build then it expands
+to a call to Log::print that prints the filename and line number,
+otherwise it expands to a call that doesn't.
+*/
+
+#if defined(NOLOGGING)
+#define LOG(_a1)
+#define LOGC(_a1, _a2)
+#define CLOG_TRACE
+#elif defined(NDEBUG)
+#define LOG(_a1) CLOG->print _a1
+#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2
+#define CLOG_TRACE NULL, 0,
+#else
+#define LOG(_a1) CLOG->print _a1
+#define LOGC(_a1, _a2) if (_a1) CLOG->print _a2
+#define CLOG_TRACE __FILE__, __LINE__,
+#endif
+
+// the CLOG_* defines are line and file plus %z and an octal number (060=0,
+// 071=9), but the limitation is that once we run out of numbers at either
+// end, then we resort to using non-numerical chars. this still works (since
+// to deduce the number we subtract octal \060, so '/' is -1, and ':' is 10
+
+#define CLOG_PRINT CLOG_TRACE "%z\057" // char is '/'
+#define CLOG_CRIT CLOG_TRACE "%z\060" // char is '0'
+#define CLOG_ERR CLOG_TRACE "%z\061"
+#define CLOG_WARN CLOG_TRACE "%z\062"
+#define CLOG_NOTE CLOG_TRACE "%z\063"
+#define CLOG_INFO CLOG_TRACE "%z\064"
+#define CLOG_DEBUG CLOG_TRACE "%z\065"
+#define CLOG_DEBUG1 CLOG_TRACE "%z\066"
+#define CLOG_DEBUG2 CLOG_TRACE "%z\067"
+#define CLOG_DEBUG3 CLOG_TRACE "%z\070"
+#define CLOG_DEBUG4 CLOG_TRACE "%z\071" // char is '9'
+#define CLOG_DEBUG5 CLOG_TRACE "%z\072" // char is ':'
diff --git a/src/lib/base/NonBlockingStream.cpp b/src/lib/base/NonBlockingStream.cpp
new file mode 100644
index 0000000..d44add1
--- /dev/null
+++ b/src/lib/base/NonBlockingStream.cpp
@@ -0,0 +1,60 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Debauchee Open Source Group
+ *
+ * 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/>.
+ */
+
+#if !defined(_WIN32)
+
+#include "base/NonBlockingStream.h"
+
+#include <unistd.h> // tcgetattr/tcsetattr, read
+#include <termios.h> // tcgetattr/tcsetattr
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+NonBlockingStream::NonBlockingStream(int fd) :
+ _fd(fd)
+{
+ // disable ICANON & ECHO so we don't have to wait for a newline
+ // before we get data (and to keep it from being echoed back out)
+ termios ta;
+ tcgetattr(fd, &ta);
+ _p_ta_previous = new termios(ta);
+ ta.c_lflag &= ~(ICANON | ECHO);
+ tcsetattr(fd, TCSANOW, &ta);
+
+ // prevent IO from blocking so we can poll (read())
+ int _cntl_previous = fcntl(fd, F_GETFL);
+ fcntl(fd, F_SETFL, _cntl_previous | O_NONBLOCK);
+}
+
+NonBlockingStream::~NonBlockingStream()
+{
+ tcsetattr(_fd, TCSANOW, _p_ta_previous);
+ fcntl(_fd, F_SETFL, _cntl_previous);
+ delete _p_ta_previous;
+}
+
+bool NonBlockingStream::try_read_char(char &ch) const
+{
+ int result = read(_fd, &ch, 1);
+ if (result == 1)
+ return true;
+ assert(result == -1 && (errno == EAGAIN || errno == EWOULDBLOCK));
+ return false;
+}
+
+#endif // !defined(_WIN32)
diff --git a/src/lib/base/NonBlockingStream.h b/src/lib/base/NonBlockingStream.h
new file mode 100644
index 0000000..4c27762
--- /dev/null
+++ b/src/lib/base/NonBlockingStream.h
@@ -0,0 +1,49 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2008 Debauchee Open Source Group
+ *
+ * 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
+
+// windows doesn't have a unistd.h so this class won't work as-written.
+// at the moment barrier doesn't need this functionality on windows so
+// it's left as a stub to be optimized out
+#if defined(_WIN32)
+
+class NonBlockingStream
+{
+public:
+ bool try_read_char(char &ch) const { return false; };
+};
+
+#else // non-windows platforms
+
+struct termios;
+
+class NonBlockingStream
+{
+public:
+ explicit NonBlockingStream(int fd = 0);
+ ~NonBlockingStream();
+
+ bool try_read_char(char &ch) const;
+
+private:
+ int _fd;
+ termios * _p_ta_previous;
+ int _cntl_previous;
+};
+
+#endif
diff --git a/src/lib/base/PriorityQueue.h b/src/lib/base/PriorityQueue.h
new file mode 100644
index 0000000..d2ca70e
--- /dev/null
+++ b/src/lib/base/PriorityQueue.h
@@ -0,0 +1,138 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "common/stdvector.h"
+
+#include <algorithm>
+#include <functional>
+
+//! A priority queue with an iterator
+/*!
+This priority queue is the same as a standard priority queue except:
+it sorts by std::greater, it has a forward iterator through the elements
+(which can appear in any order), and its contents can be swapped.
+*/
+template <class T, class Container = std::vector<T>,
+#if defined(_MSC_VER)
+ class Compare = std::greater<Container::value_type> >
+#else
+ class Compare = std::greater<typename Container::value_type> >
+#endif
+class PriorityQueue {
+public:
+ typedef typename Container::value_type value_type;
+ typedef typename Container::size_type size_type;
+ typedef typename Container::iterator iterator;
+ typedef typename Container::const_iterator const_iterator;
+ typedef Container container_type;
+
+ PriorityQueue() { }
+ PriorityQueue(Container& swappedIn) { swap(swappedIn); }
+ ~PriorityQueue() { }
+
+ //! @name manipulators
+ //@{
+
+ //! Add element
+ void push(const value_type& v)
+ {
+ c.push_back(v);
+ std::push_heap(c.begin(), c.end(), comp);
+ }
+
+ //! Remove head element
+ void pop()
+ {
+ std::pop_heap(c.begin(), c.end(), comp);
+ c.pop_back();
+ }
+
+ //! Erase element
+ void erase(iterator i)
+ {
+ c.erase(i);
+ std::make_heap(c.begin(), c.end(), comp);
+ }
+
+ //! Get start iterator
+ iterator begin()
+ {
+ return c.begin();
+ }
+
+ //! Get end iterator
+ iterator end()
+ {
+ return c.end();
+ }
+
+ //! Swap contents with another priority queue
+ void swap(PriorityQueue<T, Container, Compare>& q)
+ {
+ c.swap(q.c);
+ }
+
+ //! Swap contents with another container
+ void swap(Container& c2)
+ {
+ c.swap(c2);
+ std::make_heap(c.begin(), c.end(), comp);
+ }
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Returns true if there are no elements
+ bool empty() const
+ {
+ return c.empty();
+ }
+
+ //! Returns the number of elements
+ size_type size() const
+ {
+ return c.size();
+ }
+
+ //! Returns the head element
+ const value_type& top() const
+ {
+ return c.front();
+ }
+
+ //! Get start iterator
+ const_iterator begin() const
+ {
+ return c.begin();
+ }
+
+ //! Get end iterator
+ const_iterator end() const
+ {
+ return c.end();
+ }
+
+ //@}
+
+private:
+ Container c;
+ Compare comp;
+};
diff --git a/src/lib/base/SimpleEventQueueBuffer.cpp b/src/lib/base/SimpleEventQueueBuffer.cpp
new file mode 100644
index 0000000..b55fe55
--- /dev/null
+++ b/src/lib/base/SimpleEventQueueBuffer.cpp
@@ -0,0 +1,101 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "base/SimpleEventQueueBuffer.h"
+#include "base/Stopwatch.h"
+#include "arch/Arch.h"
+
+class EventQueueTimer { };
+
+//
+// SimpleEventQueueBuffer
+//
+
+SimpleEventQueueBuffer::SimpleEventQueueBuffer()
+{
+ m_queueMutex = ARCH->newMutex();
+ m_queueReadyCond = ARCH->newCondVar();
+ m_queueReady = false;
+}
+
+SimpleEventQueueBuffer::~SimpleEventQueueBuffer()
+{
+ ARCH->closeCondVar(m_queueReadyCond);
+ ARCH->closeMutex(m_queueMutex);
+}
+
+void
+SimpleEventQueueBuffer::waitForEvent(double timeout)
+{
+ ArchMutexLock lock(m_queueMutex);
+ Stopwatch timer(true);
+ while (!m_queueReady) {
+ double timeLeft = timeout;
+ if (timeLeft >= 0.0) {
+ timeLeft -= timer.getTime();
+ if (timeLeft < 0.0) {
+ return;
+ }
+ }
+ ARCH->waitCondVar(m_queueReadyCond, m_queueMutex, timeLeft);
+ }
+}
+
+IEventQueueBuffer::Type
+SimpleEventQueueBuffer::getEvent(Event&, UInt32& dataID)
+{
+ ArchMutexLock lock(m_queueMutex);
+ if (!m_queueReady) {
+ return kNone;
+ }
+ dataID = m_queue.back();
+ m_queue.pop_back();
+ m_queueReady = !m_queue.empty();
+ return kUser;
+}
+
+bool
+SimpleEventQueueBuffer::addEvent(UInt32 dataID)
+{
+ ArchMutexLock lock(m_queueMutex);
+ m_queue.push_front(dataID);
+ if (!m_queueReady) {
+ m_queueReady = true;
+ ARCH->broadcastCondVar(m_queueReadyCond);
+ }
+ return true;
+}
+
+bool
+SimpleEventQueueBuffer::isEmpty() const
+{
+ ArchMutexLock lock(m_queueMutex);
+ return !m_queueReady;
+}
+
+EventQueueTimer*
+SimpleEventQueueBuffer::newTimer(double, bool) const
+{
+ return new EventQueueTimer;
+}
+
+void
+SimpleEventQueueBuffer::deleteTimer(EventQueueTimer* timer) const
+{
+ delete timer;
+}
diff --git a/src/lib/base/SimpleEventQueueBuffer.h b/src/lib/base/SimpleEventQueueBuffer.h
new file mode 100644
index 0000000..4aa76d3
--- /dev/null
+++ b/src/lib/base/SimpleEventQueueBuffer.h
@@ -0,0 +1,52 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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/IEventQueueBuffer.h"
+#include "arch/IArchMultithread.h"
+#include "common/stddeque.h"
+
+//! In-memory event queue buffer
+/*!
+An event queue buffer provides a queue of events for an IEventQueue.
+*/
+class SimpleEventQueueBuffer : public IEventQueueBuffer {
+public:
+ SimpleEventQueueBuffer();
+ ~SimpleEventQueueBuffer();
+
+ // IEventQueueBuffer overrides
+ void init() { }
+ virtual void waitForEvent(double timeout);
+ virtual Type getEvent(Event& event, UInt32& dataID);
+ virtual bool addEvent(UInt32 dataID);
+ virtual bool isEmpty() const;
+ virtual EventQueueTimer*
+ newTimer(double duration, bool oneShot) const;
+ virtual void deleteTimer(EventQueueTimer*) const;
+
+private:
+ typedef std::deque<UInt32> EventDeque;
+
+ ArchMutex m_queueMutex;
+ ArchCond m_queueReadyCond;
+ bool m_queueReady;
+ EventDeque m_queue;
+};
+
diff --git a/src/lib/base/Stopwatch.cpp b/src/lib/base/Stopwatch.cpp
new file mode 100644
index 0000000..b9ceb85
--- /dev/null
+++ b/src/lib/base/Stopwatch.cpp
@@ -0,0 +1,130 @@
+/*
+ * barrier -- 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 "base/Stopwatch.h"
+#include "arch/Arch.h"
+
+//
+// Stopwatch
+//
+
+Stopwatch::Stopwatch(bool triggered) :
+ m_mark(0.0),
+ m_triggered(triggered),
+ m_stopped(triggered)
+{
+ if (!triggered) {
+ m_mark = ARCH->time();
+ }
+}
+
+Stopwatch::~Stopwatch()
+{
+ // do nothing
+}
+
+double
+Stopwatch::reset()
+{
+ if (m_stopped) {
+ const double dt = m_mark;
+ m_mark = 0.0;
+ return dt;
+ }
+ else {
+ const double t = ARCH->time();
+ const double dt = t - m_mark;
+ m_mark = t;
+ return dt;
+ }
+}
+
+void
+Stopwatch::stop()
+{
+ if (m_stopped) {
+ return;
+ }
+
+ // save the elapsed time
+ m_mark = ARCH->time() - m_mark;
+ m_stopped = true;
+}
+
+void
+Stopwatch::start()
+{
+ m_triggered = false;
+ if (!m_stopped) {
+ return;
+ }
+
+ // set the mark such that it reports the time elapsed at stop()
+ m_mark = ARCH->time() - m_mark;
+ m_stopped = false;
+}
+
+void
+Stopwatch::setTrigger()
+{
+ stop();
+ m_triggered = true;
+}
+
+double
+Stopwatch::getTime()
+{
+ if (m_triggered) {
+ const double dt = m_mark;
+ start();
+ return dt;
+ }
+ else if (m_stopped) {
+ return m_mark;
+ }
+ else {
+ return ARCH->time() - m_mark;
+ }
+}
+
+Stopwatch::operator double()
+{
+ return getTime();
+}
+
+bool
+Stopwatch::isStopped() const
+{
+ return m_stopped;
+}
+
+double
+Stopwatch::getTime() const
+{
+ if (m_stopped) {
+ return m_mark;
+ }
+ else {
+ return ARCH->time() - m_mark;
+ }
+}
+
+Stopwatch::operator double() const
+{
+ return getTime();
+}
diff --git a/src/lib/base/Stopwatch.h b/src/lib/base/Stopwatch.h
new file mode 100644
index 0000000..dda74ea
--- /dev/null
+++ b/src/lib/base/Stopwatch.h
@@ -0,0 +1,109 @@
+/*
+ * barrier -- 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 "common/common.h"
+
+//! A timer class
+/*!
+This class measures time intervals. All time interval measurement
+should use this class.
+*/
+class Stopwatch {
+public:
+ /*!
+ The default constructor does an implicit reset() or setTrigger().
+ If triggered == false then the clock starts ticking.
+ */
+ Stopwatch(bool triggered = false);
+ ~Stopwatch();
+
+ //! @name manipulators
+ //@{
+
+ //! Reset the timer to zero
+ /*!
+ Set the start time to the current time, returning the time since
+ the last reset. This does not remove the trigger if it's set nor
+ does it start a stopped clock. If the clock is stopped then
+ subsequent reset()'s will return 0.
+ */
+ double reset();
+
+ //! Stop the timer
+ /*!
+ Stop the stopwatch. The time interval while stopped is not
+ counted by the stopwatch. stop() does not remove the trigger.
+ Has no effect if already stopped.
+ */
+ void stop();
+
+ //! Start the timer
+ /*!
+ Start the stopwatch. start() removes the trigger, even if the
+ stopwatch was already started.
+ */
+ void start();
+
+ //! Stop the timer and set the trigger
+ /*!
+ setTrigger() stops the clock like stop() except there's an
+ implicit start() the next time (non-const) getTime() is called.
+ This is useful when you want the clock to start the first time
+ you check it.
+ */
+ void setTrigger();
+
+ //! Get elapsed time
+ /*!
+ Returns the time since the last reset() (or calls reset() and
+ returns zero if the trigger is set).
+ */
+ double getTime();
+ //! Same as getTime()
+ operator double();
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Check if timer is stopped
+ /*!
+ Returns true if the stopwatch is stopped.
+ */
+ bool isStopped() const;
+
+ // return the time since the last reset().
+ //! Get elapsed time
+ /*!
+ Returns the time since the last reset(). This cannot trigger the
+ stopwatch to start and will not clear the trigger.
+ */
+ double getTime() const;
+ //! Same as getTime() const
+ operator double() const;
+ //@}
+
+private:
+ double getClock() const;
+
+private:
+ double m_mark;
+ bool m_triggered;
+ bool m_stopped;
+};
diff --git a/src/lib/base/String.cpp b/src/lib/base/String.cpp
new file mode 100644
index 0000000..97b8997
--- /dev/null
+++ b/src/lib/base/String.cpp
@@ -0,0 +1,295 @@
+/*
+ * barrier -- 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 "arch/Arch.h"
+#include "base/String.h"
+#include "common/common.h"
+#include "common/stdvector.h"
+
+#include <cctype>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <algorithm>
+#include <stdio.h>
+#include <cstdarg>
+#include <sstream>
+#include <iomanip>
+#include <algorithm>
+#include <cerrno>
+
+namespace barrier {
+namespace string {
+
+String
+format(const char* fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ String result = vformat(fmt, args);
+ va_end(args);
+ return result;
+}
+
+String
+vformat(const char* fmt, va_list args)
+{
+ // find highest indexed substitution and the locations of substitutions
+ std::vector<size_t> pos;
+ std::vector<size_t> width;
+ std::vector<size_t> index;
+ size_t maxIndex = 0;
+ for (const char* scan = fmt; *scan != '\0'; ++scan) {
+ if (*scan == '%') {
+ ++scan;
+ if (*scan == '\0') {
+ break;
+ }
+ else if (*scan == '%') {
+ // literal
+ index.push_back(0);
+ pos.push_back(static_cast<size_t>((scan - 1) - fmt));
+ width.push_back(2);
+ }
+ else if (*scan == '{') {
+ // get argument index
+ char* end;
+ errno = 0;
+ long i = strtol(scan + 1, &end, 10);
+ if (errno || (i < 0) || (*end != '}')) {
+ // invalid index -- ignore
+ scan = end - 1; // BUG if there are digits?
+ }
+ else {
+ index.push_back(i);
+ pos.push_back(static_cast<size_t>((scan - 1) - fmt));
+ width.push_back(static_cast<size_t>((end - scan) + 2));
+ if (i > maxIndex) {
+ maxIndex = i;
+ }
+ scan = end;
+ }
+ }
+ else {
+ // improper escape -- ignore
+ }
+ }
+ }
+
+ // get args
+ std::vector<const char*> value;
+ std::vector<size_t> length;
+ value.push_back("%");
+ length.push_back(1);
+ for (int i = 0; i < maxIndex; ++i) {
+ const char* arg = va_arg(args, const char*);
+ size_t len = strlen(arg);
+ value.push_back(arg);
+ length.push_back(len);
+ }
+
+ // compute final length
+ size_t resultLength = strlen(fmt);
+ const int n = static_cast<int>(pos.size());
+ for (int i = 0; i < n; ++i) {
+ resultLength -= width[i];
+ resultLength += length[index[i]];
+ }
+
+ // substitute
+ String result;
+ result.reserve(resultLength);
+ size_t src = 0;
+ for (int i = 0; i < n; ++i) {
+ result.append(fmt + src, pos[i] - src);
+ result.append(value[index[i]]);
+ src = pos[i] + width[i];
+ }
+ result.append(fmt + src);
+
+ return result;
+}
+
+String
+sprintf(const char* fmt, ...)
+{
+ char tmp[1024];
+ char* buffer = tmp;
+ int len = (int)(sizeof(tmp) / sizeof(tmp[0]));
+ String result;
+ while (buffer != NULL) {
+ // try printing into the buffer
+ va_list args;
+ va_start(args, fmt);
+ int n = ARCH->vsnprintf(buffer, len, fmt, args);
+ va_end(args);
+
+ // if the buffer wasn't big enough then make it bigger and try again
+ if (n < 0 || n > len) {
+ if (buffer != tmp) {
+ delete[] buffer;
+ }
+ len *= 2;
+ buffer = new char[len];
+ }
+
+ // if it was big enough then save the string and don't try again
+ else {
+ result = buffer;
+ if (buffer != tmp) {
+ delete[] buffer;
+ }
+ buffer = NULL;
+ }
+ }
+
+ return result;
+}
+
+void
+findReplaceAll(
+ String& subject,
+ const String& find,
+ const String& replace)
+{
+ size_t pos = 0;
+ while ((pos = subject.find(find, pos)) != String::npos) {
+ subject.replace(pos, find.length(), replace);
+ pos += replace.length();
+ }
+}
+
+String
+removeFileExt(String filename)
+{
+ size_t dot = filename.find_last_of('.');
+
+ if (dot == String::npos) {
+ return filename;
+ }
+
+ return filename.substr(0, dot);
+}
+
+void
+toHex(String& subject, int width, const char fill)
+{
+ std::stringstream ss;
+ ss << std::hex;
+ for (unsigned int i = 0; i < subject.length(); i++) {
+ ss << std::setw(width) << std::setfill(fill) << (int)(unsigned char)subject[i];
+ }
+
+ subject = ss.str();
+}
+
+void
+uppercase(String& subject)
+{
+ std::transform(subject.begin(), subject.end(), subject.begin(), ::toupper);
+}
+
+void
+removeChar(String& subject, const char c)
+{
+ subject.erase(std::remove(subject.begin(), subject.end(), c), subject.end());
+}
+
+String
+sizeTypeToString(size_t n)
+{
+ std::stringstream ss;
+ ss << n;
+ return ss.str();
+}
+
+size_t
+stringToSizeType(String string)
+{
+ std::istringstream iss(string);
+ size_t value;
+ iss >> value;
+ return value;
+}
+
+std::vector<String>
+splitString(String string, const char c)
+{
+ std::vector<String> results;
+
+ size_t head = 0;
+ size_t separator = string.find(c);
+ while (separator != String::npos) {
+ if (head!=separator) {
+ results.push_back(string.substr(head, separator - head));
+ }
+ head = separator + 1;
+ separator = string.find(c, head);
+ }
+
+ if (head < string.size()) {
+ results.push_back(string.substr(head, string.size() - head));
+ }
+
+ return results;
+}
+
+//
+// CaselessCmp
+//
+
+bool
+CaselessCmp::cmpEqual(
+ const String::value_type& a,
+ const String::value_type& b)
+{
+ // should use std::tolower but not in all versions of libstdc++ have it
+ return tolower(a) == tolower(b);
+}
+
+bool
+CaselessCmp::cmpLess(
+ const String::value_type& a,
+ const String::value_type& b)
+{
+ // should use std::tolower but not in all versions of libstdc++ have it
+ return tolower(a) < tolower(b);
+}
+
+bool
+CaselessCmp::less(const String& a, const String& b)
+{
+ return std::lexicographical_compare(
+ a.begin(), a.end(),
+ b.begin(), b.end(),
+ &barrier::string::CaselessCmp::cmpLess);
+}
+
+bool
+CaselessCmp::equal(const String& a, const String& b)
+{
+ return !(less(a, b) || less(b, a));
+}
+
+bool
+CaselessCmp::operator()(const String& a, const String& b) const
+{
+ return less(a, b);
+}
+
+}
+}
diff --git a/src/lib/base/String.h b/src/lib/base/String.h
new file mode 100644
index 0000000..3661461
--- /dev/null
+++ b/src/lib/base/String.h
@@ -0,0 +1,135 @@
+/*
+ * barrier -- 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 "common/common.h"
+#include "common/stdstring.h"
+
+#include <stdarg.h>
+#include <vector>
+
+// use standard C++ string class for our string class
+typedef std::string String;
+
+namespace barrier {
+
+//! String utilities
+/*!
+Provides functions for string manipulation.
+*/
+namespace string {
+
+//! Format positional arguments
+/*!
+Format a string using positional arguments. fmt has literal
+characters and conversion specifications introduced by `\%':
+- \%\% -- literal `\%'
+- \%{n} -- positional element n, n a positive integer, {} are literal
+
+All arguments in the variable list are const char*. Positional
+elements are indexed from 1.
+*/
+String format(const char* fmt, ...);
+
+//! Format positional arguments
+/*!
+Same as format() except takes va_list.
+*/
+String vformat(const char* fmt, va_list);
+
+//! Print a string using sprintf-style formatting
+/*!
+Equivalent to sprintf() except the result is returned as a String.
+*/
+String sprintf(const char* fmt, ...);
+
+//! Find and replace all
+/*!
+Finds \c find inside \c subject and replaces it with \c replace
+*/
+void findReplaceAll(String& subject, const String& find, const String& replace);
+
+//! Remove file extension
+/*!
+Finds the last dot and remove all characters from the dot to the end
+*/
+String removeFileExt(String filename);
+
+//! Convert into hexdecimal
+/*!
+Convert each character in \c subject into hexdecimal form with \c width
+*/
+void toHex(String& subject, int width, const char fill = '0');
+
+//! Convert to all uppercase
+/*!
+Convert each character in \c subject to uppercase
+*/
+void uppercase(String& subject);
+
+//! Remove all specific char in suject
+/*!
+Remove all specific \c c in \c suject
+*/
+void removeChar(String& subject, const char c);
+
+//! Convert a size type to a string
+/*!
+Convert an size type to a string
+*/
+String sizeTypeToString(size_t n);
+
+//! Convert a string to a size type
+/*!
+Convert an a \c string to an size type
+*/
+size_t stringToSizeType(String string);
+
+//! Split a string into substrings
+/*!
+Split a \c string that separated by a \c c into substrings
+*/
+std::vector<String> splitString(String string, const char c);
+
+//! Case-insensitive comparisons
+/*!
+This class provides case-insensitve comparison functions.
+*/
+class CaselessCmp {
+ public:
+ //! Same as less()
+ bool operator()(const String& a, const String& b) const;
+
+ //! Returns true iff \c a is lexicographically less than \c b
+ static bool less(const String& a, const String& b);
+
+ //! Returns true iff \c a is lexicographically equal to \c b
+ static bool equal(const String& a, const String& b);
+
+ //! Returns true iff \c a is lexicographically less than \c b
+ static bool cmpLess(const String::value_type& a,
+ const String::value_type& b);
+
+ //! Returns true iff \c a is lexicographically equal to \c b
+ static bool cmpEqual(const String::value_type& a,
+ const String::value_type& b);
+};
+
+}
+}
diff --git a/src/lib/base/TMethodEventJob.h b/src/lib/base/TMethodEventJob.h
new file mode 100644
index 0000000..a65f8c9
--- /dev/null
+++ b/src/lib/base/TMethodEventJob.h
@@ -0,0 +1,71 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "IEventJob.h"
+
+//! Use a member function as an event job
+/*!
+An event job class that invokes a member function.
+*/
+template <class T>
+class TMethodEventJob : public IEventJob {
+public:
+ //! run(event) invokes \c object->method(event, arg)
+ TMethodEventJob(T* object,
+ void (T::*method)(const Event&, void*),
+ void* arg = NULL);
+ virtual ~TMethodEventJob();
+
+ // IJob overrides
+ virtual void run(const Event&);
+
+private:
+ T* m_object;
+ void (T::*m_method)(const Event&, void*);
+ void* m_arg;
+};
+
+template <class T>
+inline
+TMethodEventJob<T>::TMethodEventJob(T* object,
+ void (T::*method)(const Event&, void*), void* arg) :
+ m_object(object),
+ m_method(method),
+ m_arg(arg)
+{
+ // do nothing
+}
+
+template <class T>
+inline
+TMethodEventJob<T>::~TMethodEventJob()
+{
+ // do nothing
+}
+
+template <class T>
+inline
+void
+TMethodEventJob<T>::run(const Event& event)
+{
+ if (m_object != NULL) {
+ (m_object->*m_method)(event, m_arg);
+ }
+}
diff --git a/src/lib/base/TMethodJob.h b/src/lib/base/TMethodJob.h
new file mode 100644
index 0000000..ec88f05
--- /dev/null
+++ b/src/lib/base/TMethodJob.h
@@ -0,0 +1,68 @@
+/*
+ * barrier -- 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 "IJob.h"
+
+//! Use a function as a job
+/*!
+A job class that invokes a member function.
+*/
+template <class T>
+class TMethodJob : public IJob {
+public:
+ //! run() invokes \c object->method(arg)
+ TMethodJob(T* object, void (T::*method)(void*), void* arg = NULL);
+ virtual ~TMethodJob();
+
+ // IJob overrides
+ virtual void run();
+
+private:
+ T* m_object;
+ void (T::*m_method)(void*);
+ void* m_arg;
+};
+
+template <class T>
+inline
+TMethodJob<T>::TMethodJob(T* object, void (T::*method)(void*), void* arg) :
+ m_object(object),
+ m_method(method),
+ m_arg(arg)
+{
+ // do nothing
+}
+
+template <class T>
+inline
+TMethodJob<T>::~TMethodJob()
+{
+ // do nothing
+}
+
+template <class T>
+inline
+void
+TMethodJob<T>::run()
+{
+ if (m_object != NULL) {
+ (m_object->*m_method)(m_arg);
+ }
+}
diff --git a/src/lib/base/Unicode.cpp b/src/lib/base/Unicode.cpp
new file mode 100644
index 0000000..6a077e7
--- /dev/null
+++ b/src/lib/base/Unicode.cpp
@@ -0,0 +1,784 @@
+/*
+ * barrier -- 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 "arch/Arch.h"
+#include "base/Unicode.h"
+
+#include <cstring>
+
+//
+// local utility functions
+//
+
+inline
+static
+UInt16
+decode16(const UInt8* n, bool byteSwapped)
+{
+ union x16 {
+ UInt8 n8[2];
+ UInt16 n16;
+ } c;
+ if (byteSwapped) {
+ c.n8[0] = n[1];
+ c.n8[1] = n[0];
+ }
+ else {
+ c.n8[0] = n[0];
+ c.n8[1] = n[1];
+ }
+ return c.n16;
+}
+
+inline
+static
+UInt32
+decode32(const UInt8* n, bool byteSwapped)
+{
+ union x32 {
+ UInt8 n8[4];
+ UInt32 n32;
+ } c;
+ if (byteSwapped) {
+ c.n8[0] = n[3];
+ c.n8[1] = n[2];
+ c.n8[2] = n[1];
+ c.n8[3] = n[0];
+ }
+ else {
+ c.n8[0] = n[0];
+ c.n8[1] = n[1];
+ c.n8[2] = n[2];
+ c.n8[3] = n[3];
+ }
+ return c.n32;
+}
+
+inline
+static
+void
+resetError(bool* errors)
+{
+ if (errors != NULL) {
+ *errors = false;
+ }
+}
+
+inline
+static
+void
+setError(bool* errors)
+{
+ if (errors != NULL) {
+ *errors = true;
+ }
+}
+
+
+//
+// Unicode
+//
+
+UInt32 Unicode::s_invalid = 0x0000ffff;
+UInt32 Unicode::s_replacement = 0x0000fffd;
+
+bool
+Unicode::isUTF8(const String& src)
+{
+ // convert and test each character
+ const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
+ for (UInt32 n = (UInt32)src.size(); n > 0; ) {
+ if (fromUTF8(data, n) == s_invalid) {
+ return false;
+ }
+ }
+ return true;
+}
+
+String
+Unicode::UTF8ToUCS2(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = (UInt32)src.size();
+ String dst;
+ dst.reserve(2 * n);
+
+ // convert each character
+ const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
+ while (n > 0) {
+ UInt32 c = fromUTF8(data, n);
+ if (c == s_invalid) {
+ c = s_replacement;
+ }
+ else if (c >= 0x00010000) {
+ setError(errors);
+ c = s_replacement;
+ }
+ UInt16 ucs2 = static_cast<UInt16>(c);
+ dst.append(reinterpret_cast<const char*>(&ucs2), 2);
+ }
+
+ return dst;
+}
+
+String
+Unicode::UTF8ToUCS4(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = (UInt32)src.size();
+ String dst;
+ dst.reserve(4 * n);
+
+ // convert each character
+ const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
+ while (n > 0) {
+ UInt32 c = fromUTF8(data, n);
+ if (c == s_invalid) {
+ c = s_replacement;
+ }
+ dst.append(reinterpret_cast<const char*>(&c), 4);
+ }
+
+ return dst;
+}
+
+String
+Unicode::UTF8ToUTF16(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = (UInt32)src.size();
+ String dst;
+ dst.reserve(2 * n);
+
+ // convert each character
+ const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
+ while (n > 0) {
+ UInt32 c = fromUTF8(data, n);
+ if (c == s_invalid) {
+ c = s_replacement;
+ }
+ else if (c >= 0x00110000) {
+ setError(errors);
+ c = s_replacement;
+ }
+ if (c < 0x00010000) {
+ UInt16 ucs2 = static_cast<UInt16>(c);
+ dst.append(reinterpret_cast<const char*>(&ucs2), 2);
+ }
+ else {
+ c -= 0x00010000;
+ UInt16 utf16h = static_cast<UInt16>((c >> 10) + 0xd800);
+ UInt16 utf16l = static_cast<UInt16>((c & 0x03ff) + 0xdc00);
+ dst.append(reinterpret_cast<const char*>(&utf16h), 2);
+ dst.append(reinterpret_cast<const char*>(&utf16l), 2);
+ }
+ }
+
+ return dst;
+}
+
+String
+Unicode::UTF8ToUTF32(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // get size of input string and reserve some space in output
+ UInt32 n = (UInt32)src.size();
+ String dst;
+ dst.reserve(4 * n);
+
+ // convert each character
+ const UInt8* data = reinterpret_cast<const UInt8*>(src.c_str());
+ while (n > 0) {
+ UInt32 c = fromUTF8(data, n);
+ if (c == s_invalid) {
+ c = s_replacement;
+ }
+ else if (c >= 0x00110000) {
+ setError(errors);
+ c = s_replacement;
+ }
+ dst.append(reinterpret_cast<const char*>(&c), 4);
+ }
+
+ return dst;
+}
+
+String
+Unicode::UTF8ToText(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert to wide char
+ UInt32 size;
+ wchar_t* tmp = UTF8ToWideChar(src, size, errors);
+
+ // convert string to multibyte
+ int len = ARCH->convStringWCToMB(NULL, tmp, size, errors);
+ char* mbs = new char[len + 1];
+ ARCH->convStringWCToMB(mbs, tmp, size, errors);
+ String text(mbs, len);
+
+ // clean up
+ delete[] mbs;
+ delete[] tmp;
+
+ return text;
+}
+
+String
+Unicode::UCS2ToUTF8(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = (UInt32)src.size() >> 1;
+ return doUCS2ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
+}
+
+String
+Unicode::UCS4ToUTF8(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = (UInt32)src.size() >> 2;
+ return doUCS4ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
+}
+
+String
+Unicode::UTF16ToUTF8(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = (UInt32)src.size() >> 1;
+ return doUTF16ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
+}
+
+String
+Unicode::UTF32ToUTF8(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert
+ UInt32 n = (UInt32)src.size() >> 2;
+ return doUTF32ToUTF8(reinterpret_cast<const UInt8*>(src.data()), n, errors);
+}
+
+String
+Unicode::textToUTF8(const String& src, bool* errors)
+{
+ // default to success
+ resetError(errors);
+
+ // convert string to wide characters
+ UInt32 n = (UInt32)src.size();
+ int len = ARCH->convStringMBToWC(NULL, src.c_str(), n, errors);
+ wchar_t* wcs = new wchar_t[len + 1];
+ ARCH->convStringMBToWC(wcs, src.c_str(), n, errors);
+
+ // convert to UTF8
+ String utf8 = wideCharToUTF8(wcs, len, errors);
+
+ // clean up
+ delete[] wcs;
+
+ return utf8;
+}
+
+wchar_t*
+Unicode::UTF8ToWideChar(const String& src, UInt32& size, bool* errors)
+{
+ // convert to platform's wide character encoding
+ String tmp;
+ switch (ARCH->getWideCharEncoding()) {
+ case IArchString::kUCS2:
+ tmp = UTF8ToUCS2(src, errors);
+ size = (UInt32)tmp.size() >> 1;
+ break;
+
+ case IArchString::kUCS4:
+ tmp = UTF8ToUCS4(src, errors);
+ size = (UInt32)tmp.size() >> 2;
+ break;
+
+ case IArchString::kUTF16:
+ tmp = UTF8ToUTF16(src, errors);
+ size = (UInt32)tmp.size() >> 1;
+ break;
+
+ case IArchString::kUTF32:
+ tmp = UTF8ToUTF32(src, errors);
+ size = (UInt32)tmp.size() >> 2;
+ break;
+
+ default:
+ assert(0 && "unknown wide character encoding");
+ }
+
+ // copy to a wchar_t array
+ wchar_t* dst = new wchar_t[size];
+ ::memcpy(dst, tmp.data(), sizeof(wchar_t) * size);
+ return dst;
+}
+
+String
+Unicode::wideCharToUTF8(const wchar_t* src, UInt32 size, bool* errors)
+{
+ // convert from platform's wide character encoding.
+ // note -- this must include a wide nul character (independent of
+ // the String's nul character).
+ switch (ARCH->getWideCharEncoding()) {
+ case IArchString::kUCS2:
+ return doUCS2ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
+
+ case IArchString::kUCS4:
+ return doUCS4ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
+
+ case IArchString::kUTF16:
+ return doUTF16ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
+
+ case IArchString::kUTF32:
+ return doUTF32ToUTF8(reinterpret_cast<const UInt8*>(src), size, errors);
+
+ default:
+ assert(0 && "unknown wide character encoding");
+ return String();
+ }
+}
+
+String
+Unicode::doUCS2ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ String dst;
+ dst.reserve(n);
+
+ // check if first character is 0xfffe or 0xfeff
+ bool byteSwapped = false;
+ if (n >= 1) {
+ switch (decode16(data, false)) {
+ case 0x0000feff:
+ data += 2;
+ --n;
+ break;
+
+ case 0x0000fffe:
+ byteSwapped = true;
+ data += 2;
+ --n;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // convert each character
+ for (; n > 0; data += 2, --n) {
+ UInt32 c = decode16(data, byteSwapped);
+ toUTF8(dst, c, errors);
+ }
+
+ return dst;
+}
+
+String
+Unicode::doUCS4ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ String dst;
+ dst.reserve(n);
+
+ // check if first character is 0xfffe or 0xfeff
+ bool byteSwapped = false;
+ if (n >= 1) {
+ switch (decode32(data, false)) {
+ case 0x0000feff:
+ data += 4;
+ --n;
+ break;
+
+ case 0x0000fffe:
+ byteSwapped = true;
+ data += 4;
+ --n;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // convert each character
+ for (; n > 0; data += 4, --n) {
+ UInt32 c = decode32(data, byteSwapped);
+ toUTF8(dst, c, errors);
+ }
+
+ return dst;
+}
+
+String
+Unicode::doUTF16ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ String dst;
+ dst.reserve(n);
+
+ // check if first character is 0xfffe or 0xfeff
+ bool byteSwapped = false;
+ if (n >= 1) {
+ switch (decode16(data, false)) {
+ case 0x0000feff:
+ data += 2;
+ --n;
+ break;
+
+ case 0x0000fffe:
+ byteSwapped = true;
+ data += 2;
+ --n;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // convert each character
+ for (; n > 0; data += 2, --n) {
+ UInt32 c = decode16(data, byteSwapped);
+ if (c < 0x0000d800 || c > 0x0000dfff) {
+ toUTF8(dst, c, errors);
+ }
+ else if (n == 1) {
+ // error -- missing second word
+ setError(errors);
+ toUTF8(dst, s_replacement, NULL);
+ }
+ else if (c >= 0x0000d800 && c <= 0x0000dbff) {
+ UInt32 c2 = decode16(data, byteSwapped);
+ data += 2;
+ --n;
+ if (c2 < 0x0000dc00 || c2 > 0x0000dfff) {
+ // error -- [d800,dbff] not followed by [dc00,dfff]
+ setError(errors);
+ toUTF8(dst, s_replacement, NULL);
+ }
+ else {
+ c = (((c - 0x0000d800) << 10) | (c2 - 0x0000dc00)) + 0x00010000;
+ toUTF8(dst, c, errors);
+ }
+ }
+ else {
+ // error -- [dc00,dfff] without leading [d800,dbff]
+ setError(errors);
+ toUTF8(dst, s_replacement, NULL);
+ }
+ }
+
+ return dst;
+}
+
+String
+Unicode::doUTF32ToUTF8(const UInt8* data, UInt32 n, bool* errors)
+{
+ // make some space
+ String dst;
+ dst.reserve(n);
+
+ // check if first character is 0xfffe or 0xfeff
+ bool byteSwapped = false;
+ if (n >= 1) {
+ switch (decode32(data, false)) {
+ case 0x0000feff:
+ data += 4;
+ --n;
+ break;
+
+ case 0x0000fffe:
+ byteSwapped = true;
+ data += 4;
+ --n;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // convert each character
+ for (; n > 0; data += 4, --n) {
+ UInt32 c = decode32(data, byteSwapped);
+ if (c >= 0x00110000) {
+ setError(errors);
+ c = s_replacement;
+ }
+ toUTF8(dst, c, errors);
+ }
+
+ return dst;
+}
+
+UInt32
+Unicode::fromUTF8(const UInt8*& data, UInt32& n)
+{
+ assert(data != NULL);
+ assert(n != 0);
+
+ // compute character encoding length, checking for overlong
+ // sequences (i.e. characters that don't use the shortest
+ // possible encoding).
+ UInt32 size;
+ if (data[0] < 0x80) {
+ // 0xxxxxxx
+ size = 1;
+ }
+ else if (data[0] < 0xc0) {
+ // 10xxxxxx -- in the middle of a multibyte character. counts
+ // as one invalid character.
+ --n;
+ ++data;
+ return s_invalid;
+ }
+ else if (data[0] < 0xe0) {
+ // 110xxxxx
+ size = 2;
+ }
+ else if (data[0] < 0xf0) {
+ // 1110xxxx
+ size = 3;
+ }
+ else if (data[0] < 0xf8) {
+ // 11110xxx
+ size = 4;
+ }
+ else if (data[0] < 0xfc) {
+ // 111110xx
+ size = 5;
+ }
+ else if (data[0] < 0xfe) {
+ // 1111110x
+ size = 6;
+ }
+ else {
+ // invalid sequence. dunno how many bytes to skip so skip one.
+ --n;
+ ++data;
+ return s_invalid;
+ }
+
+ // make sure we have enough data
+ if (size > n) {
+ data += n;
+ n = 0;
+ return s_invalid;
+ }
+
+ // extract character
+ UInt32 c;
+ switch (size) {
+ case 1:
+ c = static_cast<UInt32>(data[0]);
+ break;
+
+ case 2:
+ c = ((static_cast<UInt32>(data[0]) & 0x1f) << 6) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) );
+ break;
+
+ case 3:
+ c = ((static_cast<UInt32>(data[0]) & 0x0f) << 12) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) << 6) |
+ ((static_cast<UInt32>(data[2]) & 0x3f) );
+ break;
+
+ case 4:
+ c = ((static_cast<UInt32>(data[0]) & 0x07) << 18) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) << 12) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) << 6) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) );
+ break;
+
+ case 5:
+ c = ((static_cast<UInt32>(data[0]) & 0x03) << 24) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) << 18) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) << 12) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) << 6) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) );
+ break;
+
+ case 6:
+ c = ((static_cast<UInt32>(data[0]) & 0x01) << 30) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) << 24) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) << 18) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) << 12) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) << 6) |
+ ((static_cast<UInt32>(data[1]) & 0x3f) );
+ break;
+
+ default:
+ assert(0 && "invalid size");
+ return s_invalid;
+ }
+
+ // check that all bytes after the first have the pattern 10xxxxxx.
+ // truncated sequences are treated as a single malformed character.
+ bool truncated = false;
+ switch (size) {
+ case 6:
+ if ((data[5] & 0xc0) != 0x80) {
+ truncated = true;
+ size = 5;
+ }
+ // fall through
+
+ case 5:
+ if ((data[4] & 0xc0) != 0x80) {
+ truncated = true;
+ size = 4;
+ }
+ // fall through
+
+ case 4:
+ if ((data[3] & 0xc0) != 0x80) {
+ truncated = true;
+ size = 3;
+ }
+ // fall through
+
+ case 3:
+ if ((data[2] & 0xc0) != 0x80) {
+ truncated = true;
+ size = 2;
+ }
+ // fall through
+
+ case 2:
+ if ((data[1] & 0xc0) != 0x80) {
+ truncated = true;
+ size = 1;
+ }
+ }
+
+ // update parameters
+ data += size;
+ n -= size;
+
+ // invalid if sequence was truncated
+ if (truncated) {
+ return s_invalid;
+ }
+
+ // check for characters that didn't use the smallest possible encoding
+ static UInt32 s_minChar[] = {
+ 0,
+ 0x00000000,
+ 0x00000080,
+ 0x00000800,
+ 0x00010000,
+ 0x00200000,
+ 0x04000000
+ };
+ if (c < s_minChar[size]) {
+ return s_invalid;
+ }
+
+ // check for characters not in ISO-10646
+ if (c >= 0x0000d800 && c <= 0x0000dfff) {
+ return s_invalid;
+ }
+ if (c >= 0x0000fffe && c <= 0x0000ffff) {
+ return s_invalid;
+ }
+
+ return c;
+}
+
+void
+Unicode::toUTF8(String& dst, UInt32 c, bool* errors)
+{
+ UInt8 data[6];
+
+ // handle characters outside the valid range
+ if ((c >= 0x0000d800 && c <= 0x0000dfff) || c >= 0x80000000) {
+ setError(errors);
+ c = s_replacement;
+ }
+
+ // convert to UTF-8
+ if (c < 0x00000080) {
+ data[0] = static_cast<UInt8>(c);
+ dst.append(reinterpret_cast<char*>(data), 1);
+ }
+ else if (c < 0x00000800) {
+ data[0] = static_cast<UInt8>(((c >> 6) & 0x0000001f) + 0xc0);
+ data[1] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
+ dst.append(reinterpret_cast<char*>(data), 2);
+ }
+ else if (c < 0x00010000) {
+ data[0] = static_cast<UInt8>(((c >> 12) & 0x0000000f) + 0xe0);
+ data[1] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
+ data[2] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
+ dst.append(reinterpret_cast<char*>(data), 3);
+ }
+ else if (c < 0x00200000) {
+ data[0] = static_cast<UInt8>(((c >> 18) & 0x00000007) + 0xf0);
+ data[1] = static_cast<UInt8>(((c >> 12) & 0x0000003f) + 0x80);
+ data[2] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
+ data[3] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
+ dst.append(reinterpret_cast<char*>(data), 4);
+ }
+ else if (c < 0x04000000) {
+ data[0] = static_cast<UInt8>(((c >> 24) & 0x00000003) + 0xf8);
+ data[1] = static_cast<UInt8>(((c >> 18) & 0x0000003f) + 0x80);
+ data[2] = static_cast<UInt8>(((c >> 12) & 0x0000003f) + 0x80);
+ data[3] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
+ data[4] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
+ dst.append(reinterpret_cast<char*>(data), 5);
+ }
+ else if (c < 0x80000000) {
+ data[0] = static_cast<UInt8>(((c >> 30) & 0x00000001) + 0xfc);
+ data[1] = static_cast<UInt8>(((c >> 24) & 0x0000003f) + 0x80);
+ data[2] = static_cast<UInt8>(((c >> 18) & 0x0000003f) + 0x80);
+ data[3] = static_cast<UInt8>(((c >> 12) & 0x0000003f) + 0x80);
+ data[4] = static_cast<UInt8>(((c >> 6) & 0x0000003f) + 0x80);
+ data[5] = static_cast<UInt8>((c & 0x0000003f) + 0x80);
+ dst.append(reinterpret_cast<char*>(data), 6);
+ }
+ else {
+ assert(0 && "character out of range");
+ }
+}
diff --git a/src/lib/base/Unicode.h b/src/lib/base/Unicode.h
new file mode 100644
index 0000000..1391c1e
--- /dev/null
+++ b/src/lib/base/Unicode.h
@@ -0,0 +1,144 @@
+/*
+ * barrier -- 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"
+#include "common/basic_types.h"
+
+//! Unicode utility functions
+/*!
+This class provides functions for converting between various Unicode
+encodings and the current locale encoding.
+*/
+class Unicode {
+public:
+ //! @name accessors
+ //@{
+
+ //! Test UTF-8 string for validity
+ /*!
+ Returns true iff the string contains a valid sequence of UTF-8
+ encoded characters.
+ */
+ static bool isUTF8(const String&);
+
+ //! Convert from UTF-8 to UCS-2 encoding
+ /*!
+ Convert from UTF-8 to UCS-2. If errors is not NULL then *errors
+ is set to true iff any character could not be encoded in UCS-2.
+ Decoding errors do not set *errors.
+ */
+ static String UTF8ToUCS2(const String&, bool* errors = NULL);
+
+ //! Convert from UTF-8 to UCS-4 encoding
+ /*!
+ Convert from UTF-8 to UCS-4. If errors is not NULL then *errors
+ is set to true iff any character could not be encoded in UCS-4.
+ Decoding errors do not set *errors.
+ */
+ static String UTF8ToUCS4(const String&, bool* errors = NULL);
+
+ //! Convert from UTF-8 to UTF-16 encoding
+ /*!
+ Convert from UTF-8 to UTF-16. If errors is not NULL then *errors
+ is set to true iff any character could not be encoded in UTF-16.
+ Decoding errors do not set *errors.
+ */
+ static String UTF8ToUTF16(const String&, bool* errors = NULL);
+
+ //! Convert from UTF-8 to UTF-32 encoding
+ /*!
+ Convert from UTF-8 to UTF-32. If errors is not NULL then *errors
+ is set to true iff any character could not be encoded in UTF-32.
+ Decoding errors do not set *errors.
+ */
+ static String UTF8ToUTF32(const String&, bool* errors = NULL);
+
+ //! Convert from UTF-8 to the current locale encoding
+ /*!
+ Convert from UTF-8 to the current locale encoding. If errors is not
+ NULL then *errors is set to true iff any character could not be encoded.
+ Decoding errors do not set *errors.
+ */
+ static String UTF8ToText(const String&, bool* errors = NULL);
+
+ //! Convert from UCS-2 to UTF-8
+ /*!
+ Convert from UCS-2 to UTF-8. If errors is not NULL then *errors is
+ set to true iff any character could not be decoded.
+ */
+ static String UCS2ToUTF8(const String&, bool* errors = NULL);
+
+ //! Convert from UCS-4 to UTF-8
+ /*!
+ Convert from UCS-4 to UTF-8. If errors is not NULL then *errors is
+ set to true iff any character could not be decoded.
+ */
+ static String UCS4ToUTF8(const String&, bool* errors = NULL);
+
+ //! Convert from UTF-16 to UTF-8
+ /*!
+ Convert from UTF-16 to UTF-8. If errors is not NULL then *errors is
+ set to true iff any character could not be decoded.
+ */
+ static String UTF16ToUTF8(const String&, bool* errors = NULL);
+
+ //! Convert from UTF-32 to UTF-8
+ /*!
+ Convert from UTF-32 to UTF-8. If errors is not NULL then *errors is
+ set to true iff any character could not be decoded.
+ */
+ static String UTF32ToUTF8(const String&, bool* errors = NULL);
+
+ //! Convert from the current locale encoding to UTF-8
+ /*!
+ Convert from the current locale encoding to UTF-8. If errors is not
+ NULL then *errors is set to true iff any character could not be decoded.
+ */
+ static String textToUTF8(const String&, bool* errors = NULL);
+
+ //@}
+
+private:
+ // convert UTF8 to wchar_t string (using whatever encoding is native
+ // to the platform). caller must delete[] the returned string. the
+ // string is *not* nul terminated; the length (in characters) is
+ // returned in size.
+ static wchar_t* UTF8ToWideChar(const String&,
+ UInt32& size, bool* errors);
+
+ // convert nul terminated wchar_t string (in platform's native
+ // encoding) to UTF8.
+ static String wideCharToUTF8(const wchar_t*,
+ UInt32 size, bool* errors);
+
+ // internal conversion to UTF8
+ static String doUCS2ToUTF8(const UInt8* src, UInt32 n, bool* errors);
+ static String doUCS4ToUTF8(const UInt8* src, UInt32 n, bool* errors);
+ static String doUTF16ToUTF8(const UInt8* src, UInt32 n, bool* errors);
+ static String doUTF32ToUTF8(const UInt8* src, UInt32 n, bool* errors);
+
+ // convert characters to/from UTF8
+ static UInt32 fromUTF8(const UInt8*& src, UInt32& size);
+ static void toUTF8(String& dst, UInt32 c, bool* errors);
+
+private:
+ static UInt32 s_invalid;
+ static UInt32 s_replacement;
+};
diff --git a/src/lib/base/XBase.cpp b/src/lib/base/XBase.cpp
new file mode 100644
index 0000000..29ae927
--- /dev/null
+++ b/src/lib/base/XBase.cpp
@@ -0,0 +1,76 @@
+/*
+ * barrier -- 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 "base/XBase.h"
+#include "base/String.h"
+
+#include <cerrno>
+#include <cstdarg>
+
+//
+// XBase
+//
+
+XBase::XBase() :
+ std::runtime_error("")
+{
+ // do nothing
+}
+
+XBase::XBase(const String& msg) :
+ std::runtime_error(msg)
+{
+ // do nothing
+}
+
+XBase::~XBase() _NOEXCEPT
+{
+ // do nothing
+}
+
+const char*
+XBase::what() const _NOEXCEPT
+{
+ const char* what = std::runtime_error::what();
+ if (strlen(what) == 0) {
+ m_what = getWhat();
+ return m_what.c_str();
+ }
+ return what;
+}
+
+String
+XBase::format(const char* /*id*/, const char* fmt, ...) const throw()
+{
+ // FIXME -- lookup message string using id as an index. set
+ // fmt to that string if it exists.
+
+ // format
+ String result;
+ va_list args;
+ va_start(args, fmt);
+ try {
+ result = barrier::string::vformat(fmt, args);
+ }
+ catch (...) {
+ // ignore
+ }
+ va_end(args);
+
+ return result;
+}
diff --git a/src/lib/base/XBase.h b/src/lib/base/XBase.h
new file mode 100644
index 0000000..3064b6c
--- /dev/null
+++ b/src/lib/base/XBase.h
@@ -0,0 +1,125 @@
+/*
+ * barrier -- 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"
+#include "common/stdexcept.h"
+
+//! Exception base class
+/*!
+This is the base class of most exception types.
+*/
+class XBase : public std::runtime_error {
+public:
+ //! Use getWhat() as the result of what()
+ XBase();
+ //! Use \c msg as the result of what()
+ XBase(const String& msg);
+ virtual ~XBase() _NOEXCEPT;
+
+ //! Reason for exception
+ virtual const char* what() const _NOEXCEPT;
+
+protected:
+ //! Get a human readable string describing the exception
+ virtual String getWhat() const throw() { return ""; }
+
+ //! Format a string
+ /*!
+ Looks up a message format using \c id, using \c defaultFormat if
+ no format can be found, then replaces positional parameters in
+ the format string and returns the result.
+ */
+ virtual String format(const char* id,
+ const char* defaultFormat, ...) const throw();
+private:
+ mutable String m_what;
+};
+
+/*!
+\def XBASE_SUBCLASS
+Convenience macro to subclass from XBase (or a subclass of it),
+providing the c'tor taking a const String&. getWhat() is not
+declared.
+*/
+#define XBASE_SUBCLASS(name_, super_) \
+class name_ : public super_ { \
+public: \
+ name_() : super_() { } \
+ name_(const String& msg) : super_(msg) { } \
+ virtual ~name_() _NOEXCEPT { } \
+}
+
+/*!
+\def XBASE_SUBCLASS
+Convenience macro to subclass from XBase (or a subclass of it),
+providing the c'tor taking a const String&. getWhat() must be
+implemented.
+*/
+#define XBASE_SUBCLASS_WHAT(name_, super_) \
+class name_ : public super_ { \
+public: \
+ name_() : super_() { } \
+ name_(const String& msg) : super_(msg) { } \
+ virtual ~name_() _NOEXCEPT { } \
+ \
+protected: \
+ virtual String getWhat() const throw(); \
+}
+
+/*!
+\def XBASE_SUBCLASS_FORMAT
+Convenience macro to subclass from XBase (or a subclass of it),
+providing the c'tor taking a const String&. what() is overridden
+to call getWhat() when first called; getWhat() can format the
+error message and can call what() to get the message passed to the
+c'tor.
+*/
+#define XBASE_SUBCLASS_FORMAT(name_, super_) \
+class name_ : public super_ { \
+private: \
+ enum EState { kFirst, kFormat, kDone }; \
+ \
+public: \
+ name_() : super_(), m_state(kDone) { } \
+ name_(const String& msg) : super_(msg), m_state(kFirst) { } \
+ virtual ~name_() _NOEXCEPT { } \
+ \
+ virtual const char* what() const _NOEXCEPT \
+ { \
+ if (m_state == kFirst) { \
+ m_state = kFormat; \
+ m_formatted = getWhat(); \
+ m_state = kDone; \
+ } \
+ if (m_state == kDone) { \
+ return m_formatted.c_str(); \
+ } \
+ else { \
+ return super_::what(); \
+ } \
+ } \
+ \
+protected: \
+ virtual String getWhat() const throw(); \
+ \
+private: \
+ mutable EState m_state; \
+ mutable std::string m_formatted; \
+}
diff --git a/src/lib/base/log_outputters.cpp b/src/lib/base/log_outputters.cpp
new file mode 100644
index 0000000..8e56c26
--- /dev/null
+++ b/src/lib/base/log_outputters.cpp
@@ -0,0 +1,337 @@
+/*
+ * barrier -- 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 "base/log_outputters.h"
+#include "base/TMethodJob.h"
+#include "arch/Arch.h"
+
+#include <fstream>
+
+enum EFileLogOutputter {
+ kFileSizeLimit = 1024 // kb
+};
+
+//
+// StopLogOutputter
+//
+
+StopLogOutputter::StopLogOutputter()
+{
+ // do nothing
+}
+
+StopLogOutputter::~StopLogOutputter()
+{
+ // do nothing
+}
+
+void
+StopLogOutputter::open(const char*)
+{
+ // do nothing
+}
+
+void
+StopLogOutputter::close()
+{
+ // do nothing
+}
+
+void
+StopLogOutputter::show(bool)
+{
+ // do nothing
+}
+
+bool
+StopLogOutputter::write(ELevel, const char*)
+{
+ return false;
+}
+
+
+//
+// ConsoleLogOutputter
+//
+
+ConsoleLogOutputter::ConsoleLogOutputter()
+{
+}
+
+ConsoleLogOutputter::~ConsoleLogOutputter()
+{
+}
+
+void
+ConsoleLogOutputter::open(const char* title)
+{
+ ARCH->openConsole(title);
+}
+
+void
+ConsoleLogOutputter::close()
+{
+ ARCH->closeConsole();
+}
+
+void
+ConsoleLogOutputter::show(bool showIfEmpty)
+{
+ ARCH->showConsole(showIfEmpty);
+}
+
+bool
+ConsoleLogOutputter::write(ELevel level, const char* msg)
+{
+ ARCH->writeConsole(level, msg);
+ return true;
+}
+
+void
+ConsoleLogOutputter::flush()
+{
+
+}
+
+
+//
+// SystemLogOutputter
+//
+
+SystemLogOutputter::SystemLogOutputter()
+{
+ // do nothing
+}
+
+SystemLogOutputter::~SystemLogOutputter()
+{
+ // do nothing
+}
+
+void
+SystemLogOutputter::open(const char* title)
+{
+ ARCH->openLog(title);
+}
+
+void
+SystemLogOutputter::close()
+{
+ ARCH->closeLog();
+}
+
+void
+SystemLogOutputter::show(bool showIfEmpty)
+{
+ ARCH->showLog(showIfEmpty);
+}
+
+bool
+SystemLogOutputter::write(ELevel level, const char* msg)
+{
+ ARCH->writeLog(level, msg);
+ return true;
+}
+
+//
+// SystemLogger
+//
+
+SystemLogger::SystemLogger(const char* title, bool blockConsole) :
+ m_stop(NULL)
+{
+ // redirect log messages
+ if (blockConsole) {
+ m_stop = new StopLogOutputter;
+ CLOG->insert(m_stop);
+ }
+ m_syslog = new SystemLogOutputter;
+ m_syslog->open(title);
+ CLOG->insert(m_syslog);
+}
+
+SystemLogger::~SystemLogger()
+{
+ CLOG->remove(m_syslog);
+ delete m_syslog;
+ if (m_stop != NULL) {
+ CLOG->remove(m_stop);
+ delete m_stop;
+ }
+}
+
+
+//
+// BufferedLogOutputter
+//
+
+BufferedLogOutputter::BufferedLogOutputter(UInt32 maxBufferSize) :
+ m_maxBufferSize(maxBufferSize)
+{
+ // do nothing
+}
+
+BufferedLogOutputter::~BufferedLogOutputter()
+{
+ // do nothing
+}
+
+BufferedLogOutputter::const_iterator
+BufferedLogOutputter::begin() const
+{
+ return m_buffer.begin();
+}
+
+BufferedLogOutputter::const_iterator
+BufferedLogOutputter::end() const
+{
+ return m_buffer.end();
+}
+
+void
+BufferedLogOutputter::open(const char*)
+{
+ // do nothing
+}
+
+void
+BufferedLogOutputter::close()
+{
+ // remove all elements from the buffer
+ m_buffer.clear();
+}
+
+void
+BufferedLogOutputter::show(bool)
+{
+ // do nothing
+}
+
+bool
+BufferedLogOutputter::write(ELevel, const char* message)
+{
+ while (m_buffer.size() >= m_maxBufferSize) {
+ m_buffer.pop_front();
+ }
+ m_buffer.push_back(String(message));
+ return true;
+}
+
+
+//
+// FileLogOutputter
+//
+
+FileLogOutputter::FileLogOutputter(const char* logFile)
+{
+ setLogFilename(logFile);
+}
+
+FileLogOutputter::~FileLogOutputter()
+{
+}
+
+void
+FileLogOutputter::setLogFilename(const char* logFile)
+{
+ assert(logFile != NULL);
+ m_fileName = logFile;
+}
+
+bool
+FileLogOutputter::write(ELevel level, const char *message)
+{
+ bool moveFile = false;
+
+ std::ofstream m_handle;
+ m_handle.open(m_fileName.c_str(), std::fstream::app);
+ if (m_handle.is_open() && m_handle.fail() != true) {
+ m_handle << message << std::endl;
+
+ // when file size exceeds limits, move to 'old log' filename.
+ size_t p = m_handle.tellp();
+ if (p > (kFileSizeLimit * 1024)) {
+ moveFile = true;
+ }
+ }
+ m_handle.close();
+
+ if (moveFile) {
+ String oldLogFilename = barrier::string::sprintf("%s.1", m_fileName.c_str());
+ remove(oldLogFilename.c_str());
+ rename(m_fileName.c_str(), oldLogFilename.c_str());
+ }
+
+ return true;
+}
+
+void
+FileLogOutputter::open(const char *title) {}
+
+void
+FileLogOutputter::close() {}
+
+void
+FileLogOutputter::show(bool showIfEmpty) {}
+
+//
+// MesssageBoxLogOutputter
+//
+
+MesssageBoxLogOutputter::MesssageBoxLogOutputter()
+{
+ // do nothing
+}
+
+MesssageBoxLogOutputter::~MesssageBoxLogOutputter()
+{
+ // do nothing
+}
+
+void
+MesssageBoxLogOutputter::open(const char* title)
+{
+ // do nothing
+}
+
+void
+MesssageBoxLogOutputter::close()
+{
+ // do nothing
+}
+
+void
+MesssageBoxLogOutputter::show(bool showIfEmpty)
+{
+ // do nothing
+}
+
+bool
+MesssageBoxLogOutputter::write(ELevel level, const char* msg)
+{
+ // don't spam user with messages.
+ if (level > kERROR) {
+ return true;
+ }
+
+#if SYSAPI_WIN32
+ MessageBox(NULL, msg, CLOG->getFilterName(level), MB_OK);
+#endif
+
+ return true;
+}
diff --git a/src/lib/base/log_outputters.h b/src/lib/base/log_outputters.h
new file mode 100644
index 0000000..c4940aa
--- /dev/null
+++ b/src/lib/base/log_outputters.h
@@ -0,0 +1,172 @@
+/*
+ * barrier -- 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 "mt/Thread.h"
+#include "base/ILogOutputter.h"
+#include "base/String.h"
+#include "common/basic_types.h"
+#include "common/stddeque.h"
+
+#include <list>
+#include <fstream>
+
+//! Stop traversing log chain outputter
+/*!
+This outputter performs no output and returns false from \c write(),
+causing the logger to stop traversing the outputter chain. Insert
+this to prevent already inserted outputters from writing.
+*/
+class StopLogOutputter : public ILogOutputter {
+public:
+ StopLogOutputter();
+ virtual ~StopLogOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+};
+
+//! Write log to console
+/*!
+This outputter writes output to the console. The level for each
+message is ignored.
+*/
+class ConsoleLogOutputter : public ILogOutputter {
+public:
+ ConsoleLogOutputter();
+ virtual ~ConsoleLogOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+ virtual void flush();
+};
+
+//! Write log to file
+/*!
+This outputter writes output to the file. The level for each
+message is ignored.
+*/
+
+class FileLogOutputter : public ILogOutputter {
+public:
+ FileLogOutputter(const char* logFile);
+ virtual ~FileLogOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+
+ void setLogFilename(const char* title);
+
+private:
+ std::string m_fileName;
+};
+
+//! Write log to system log
+/*!
+This outputter writes output to the system log.
+*/
+class SystemLogOutputter : public ILogOutputter {
+public:
+ SystemLogOutputter();
+ virtual ~SystemLogOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+};
+
+//! Write log to system log only
+/*!
+Creating an object of this type inserts a StopLogOutputter followed
+by a SystemLogOutputter into Log. The destructor removes those
+outputters. Add one of these to any scope that needs to write to
+the system log (only) and restore the old outputters when exiting
+the scope.
+*/
+class SystemLogger {
+public:
+ SystemLogger(const char* title, bool blockConsole);
+ ~SystemLogger();
+
+private:
+ ILogOutputter* m_syslog;
+ ILogOutputter* m_stop;
+};
+
+//! Save log history
+/*!
+This outputter records the last N log messages.
+*/
+class BufferedLogOutputter : public ILogOutputter {
+private:
+ typedef std::deque<String> Buffer;
+
+public:
+ typedef Buffer::const_iterator const_iterator;
+
+ BufferedLogOutputter(UInt32 maxBufferSize);
+ virtual ~BufferedLogOutputter();
+
+ //! @name accessors
+ //@{
+
+ //! Get start of buffer
+ const_iterator begin() const;
+
+ //! Get end of buffer
+ const_iterator end() const;
+
+ //@}
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+private:
+ UInt32 m_maxBufferSize;
+ Buffer m_buffer;
+};
+
+//! Write log to message box
+/*!
+The level for each message is ignored.
+*/
+class MesssageBoxLogOutputter : public ILogOutputter {
+public:
+ MesssageBoxLogOutputter();
+ virtual ~MesssageBoxLogOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+};
diff --git a/src/lib/client/CMakeLists.txt b/src/lib/client/CMakeLists.txt
new file mode 100644
index 0000000..97dc9db
--- /dev/null
+++ b/src/lib/client/CMakeLists.txt
@@ -0,0 +1,28 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(client STATIC ${sources})
+
+if (UNIX)
+ target_link_libraries(client synlib io)
+endif()
diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp
new file mode 100644
index 0000000..2158ee2
--- /dev/null
+++ b/src/lib/client/Client.cpp
@@ -0,0 +1,836 @@
+/*
+ * barrier -- 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 "client/Client.h"
+
+#include "client/ServerProxy.h"
+#include "barrier/Screen.h"
+#include "barrier/FileChunk.h"
+#include "barrier/DropHelper.h"
+#include "barrier/PacketStreamFilter.h"
+#include "barrier/ProtocolUtil.h"
+#include "barrier/protocol_types.h"
+#include "barrier/XBarrier.h"
+#include "barrier/StreamChunker.h"
+#include "barrier/IPlatformScreen.h"
+#include "mt/Thread.h"
+#include "net/TCPSocket.h"
+#include "net/IDataSocket.h"
+#include "net/ISocketFactory.h"
+#include "net/SecureSocket.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+#include "base/TMethodJob.h"
+#include "common/stdexcept.h"
+
+#include <cstring>
+#include <cstdlib>
+#include <sstream>
+#include <fstream>
+
+//
+// Client
+//
+
+Client::Client(
+ IEventQueue* events,
+ const String& name, const NetworkAddress& address,
+ ISocketFactory* socketFactory,
+ barrier::Screen* screen,
+ ClientArgs const& args) :
+ m_mock(false),
+ m_name(name),
+ m_serverAddress(address),
+ m_socketFactory(socketFactory),
+ m_screen(screen),
+ m_stream(NULL),
+ m_timer(NULL),
+ m_server(NULL),
+ m_ready(false),
+ m_active(false),
+ m_suspended(false),
+ m_connectOnResume(false),
+ m_events(events),
+ m_sendFileThread(NULL),
+ m_writeToDropDirThread(NULL),
+ m_socket(NULL),
+ m_useSecureNetwork(args.m_enableCrypto),
+ m_args(args),
+ m_enableClipboard(true)
+{
+ assert(m_socketFactory != NULL);
+ assert(m_screen != NULL);
+
+ // register suspend/resume event handlers
+ m_events->adoptHandler(m_events->forIScreen().suspend(),
+ getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleSuspend));
+ m_events->adoptHandler(m_events->forIScreen().resume(),
+ getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleResume));
+
+ if (m_args.m_enableDragDrop) {
+ m_events->adoptHandler(m_events->forFile().fileChunkSending(),
+ this,
+ new TMethodEventJob<Client>(this,
+ &Client::handleFileChunkSending));
+ m_events->adoptHandler(m_events->forFile().fileRecieveCompleted(),
+ this,
+ new TMethodEventJob<Client>(this,
+ &Client::handleFileRecieveCompleted));
+ }
+}
+
+Client::~Client()
+{
+ if (m_mock) {
+ return;
+ }
+
+ m_events->removeHandler(m_events->forIScreen().suspend(),
+ getEventTarget());
+ m_events->removeHandler(m_events->forIScreen().resume(),
+ getEventTarget());
+
+ cleanupTimer();
+ cleanupScreen();
+ cleanupConnecting();
+ cleanupConnection();
+ delete m_socketFactory;
+}
+
+void
+Client::connect()
+{
+ if (m_stream != NULL) {
+ return;
+ }
+ if (m_suspended) {
+ m_connectOnResume = true;
+ return;
+ }
+
+ try {
+ // resolve the server hostname. do this every time we connect
+ // in case we couldn't resolve the address earlier or the address
+ // has changed (which can happen frequently if this is a laptop
+ // being shuttled between various networks). patch by Brent
+ // Priddy.
+ m_serverAddress.resolve();
+
+ // m_serverAddress will be null if the hostname address is not reolved
+ if (m_serverAddress.getAddress() != NULL) {
+ // to help users troubleshoot, show server host name (issue: 60)
+ LOG((CLOG_NOTE "connecting to '%s': %s:%i",
+ m_serverAddress.getHostname().c_str(),
+ ARCH->addrToString(m_serverAddress.getAddress()).c_str(),
+ m_serverAddress.getPort()));
+ }
+
+ // create the socket
+ IDataSocket* socket = m_socketFactory->create(
+ ARCH->getAddrFamily(m_serverAddress.getAddress()),
+ m_useSecureNetwork);
+ m_socket = dynamic_cast<TCPSocket*>(socket);
+
+ // filter socket messages, including a packetizing filter
+ m_stream = socket;
+ m_stream = new PacketStreamFilter(m_events, m_stream, true);
+
+ // connect
+ LOG((CLOG_DEBUG1 "connecting to server"));
+ setupConnecting();
+ setupTimer();
+ socket->connect(m_serverAddress);
+ }
+ catch (XBase& e) {
+ cleanupTimer();
+ cleanupConnecting();
+ cleanupStream();
+ LOG((CLOG_DEBUG1 "connection failed"));
+ sendConnectionFailedEvent(e.what());
+ return;
+ }
+}
+
+void
+Client::disconnect(const char* msg)
+{
+ m_connectOnResume = false;
+ cleanupTimer();
+ cleanupScreen();
+ cleanupConnecting();
+ cleanupConnection();
+ if (msg != NULL) {
+ sendConnectionFailedEvent(msg);
+ }
+ else {
+ sendEvent(m_events->forClient().disconnected(), NULL);
+ }
+}
+
+void
+Client::handshakeComplete()
+{
+ m_ready = true;
+ m_screen->enable();
+ sendEvent(m_events->forClient().connected(), NULL);
+}
+
+bool
+Client::isConnected() const
+{
+ return (m_server != NULL);
+}
+
+bool
+Client::isConnecting() const
+{
+ return (m_timer != NULL);
+}
+
+NetworkAddress
+Client::getServerAddress() const
+{
+ return m_serverAddress;
+}
+
+void*
+Client::getEventTarget() const
+{
+ return m_screen->getEventTarget();
+}
+
+bool
+Client::getClipboard(ClipboardID id, IClipboard* clipboard) const
+{
+ return m_screen->getClipboard(id, clipboard);
+}
+
+void
+Client::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
+{
+ m_screen->getShape(x, y, w, h);
+}
+
+void
+Client::getCursorPos(SInt32& x, SInt32& y) const
+{
+ m_screen->getCursorPos(x, y);
+}
+
+void
+Client::enter(SInt32 xAbs, SInt32 yAbs, UInt32, KeyModifierMask mask, bool)
+{
+ m_active = true;
+ m_screen->mouseMove(xAbs, yAbs);
+ m_screen->enter(mask);
+
+ if (m_sendFileThread != NULL) {
+ StreamChunker::interruptFile();
+ m_sendFileThread = NULL;
+ }
+}
+
+bool
+Client::leave()
+{
+ m_active = false;
+
+ m_screen->leave();
+
+ if (m_enableClipboard) {
+ // send clipboards that we own and that have changed
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ if (m_ownClipboard[id]) {
+ sendClipboard(id);
+ }
+ }
+ }
+
+ return true;
+}
+
+void
+Client::setClipboard(ClipboardID id, const IClipboard* clipboard)
+{
+ m_screen->setClipboard(id, clipboard);
+ m_ownClipboard[id] = false;
+ m_sentClipboard[id] = false;
+}
+
+void
+Client::grabClipboard(ClipboardID id)
+{
+ m_screen->grabClipboard(id);
+ m_ownClipboard[id] = false;
+ m_sentClipboard[id] = false;
+}
+
+void
+Client::setClipboardDirty(ClipboardID, bool)
+{
+ assert(0 && "shouldn't be called");
+}
+
+void
+Client::keyDown(KeyID id, KeyModifierMask mask, KeyButton button)
+{
+ m_screen->keyDown(id, mask, button);
+}
+
+void
+Client::keyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button)
+{
+ m_screen->keyRepeat(id, mask, count, button);
+}
+
+void
+Client::keyUp(KeyID id, KeyModifierMask mask, KeyButton button)
+{
+ m_screen->keyUp(id, mask, button);
+}
+
+void
+Client::mouseDown(ButtonID id)
+{
+ m_screen->mouseDown(id);
+}
+
+void
+Client::mouseUp(ButtonID id)
+{
+ m_screen->mouseUp(id);
+}
+
+void
+Client::mouseMove(SInt32 x, SInt32 y)
+{
+ m_screen->mouseMove(x, y);
+}
+
+void
+Client::mouseRelativeMove(SInt32 dx, SInt32 dy)
+{
+ m_screen->mouseRelativeMove(dx, dy);
+}
+
+void
+Client::mouseWheel(SInt32 xDelta, SInt32 yDelta)
+{
+ m_screen->mouseWheel(xDelta, yDelta);
+}
+
+void
+Client::screensaver(bool activate)
+{
+ m_screen->screensaver(activate);
+}
+
+void
+Client::resetOptions()
+{
+ m_screen->resetOptions();
+}
+
+void
+Client::setOptions(const OptionsList& options)
+{
+ for (OptionsList::const_iterator index = options.begin();
+ index != options.end(); ++index) {
+ const OptionID id = *index;
+ if (id == kOptionClipboardSharing) {
+ index++;
+ if (*index == static_cast<OptionValue>(false)) {
+ LOG((CLOG_NOTE "clipboard sharing is disabled"));
+ }
+ m_enableClipboard = *index;
+
+ break;
+ }
+ }
+
+ m_screen->setOptions(options);
+}
+
+String
+Client::getName() const
+{
+ return m_name;
+}
+
+void
+Client::sendClipboard(ClipboardID id)
+{
+ // note -- m_mutex must be locked on entry
+ assert(m_screen != NULL);
+ assert(m_server != NULL);
+
+ // get clipboard data. set the clipboard time to the last
+ // clipboard time before getting the data from the screen
+ // as the screen may detect an unchanged clipboard and
+ // avoid copying the data.
+ Clipboard clipboard;
+ if (clipboard.open(m_timeClipboard[id])) {
+ clipboard.close();
+ }
+ m_screen->getClipboard(id, &clipboard);
+
+ // check time
+ if (m_timeClipboard[id] == 0 ||
+ clipboard.getTime() != m_timeClipboard[id]) {
+ // save new time
+ m_timeClipboard[id] = clipboard.getTime();
+
+ // marshall the data
+ String data = clipboard.marshall();
+
+ // save and send data if different or not yet sent
+ if (!m_sentClipboard[id] || data != m_dataClipboard[id]) {
+ m_sentClipboard[id] = true;
+ m_dataClipboard[id] = data;
+ m_server->onClipboardChanged(id, &clipboard);
+ }
+ }
+}
+
+void
+Client::sendEvent(Event::Type type, void* data)
+{
+ m_events->addEvent(Event(type, getEventTarget(), data));
+}
+
+void
+Client::sendConnectionFailedEvent(const char* msg)
+{
+ FailInfo* info = new FailInfo(msg);
+ info->m_retry = true;
+ Event event(m_events->forClient().connectionFailed(), getEventTarget(), info, Event::kDontFreeData);
+ m_events->addEvent(event);
+}
+
+void
+Client::sendFileChunk(const void* data)
+{
+ FileChunk* chunk = static_cast<FileChunk*>(const_cast<void*>(data));
+ LOG((CLOG_DEBUG1 "send file chunk"));
+ assert(m_server != NULL);
+
+ // relay
+ m_server->fileChunkSending(chunk->m_chunk[0], &chunk->m_chunk[1], chunk->m_dataSize);
+}
+
+void
+Client::setupConnecting()
+{
+ assert(m_stream != NULL);
+
+ if (m_args.m_enableCrypto) {
+ m_events->adoptHandler(m_events->forIDataSocket().secureConnected(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleConnected));
+ }
+ else {
+ m_events->adoptHandler(m_events->forIDataSocket().connected(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleConnected));
+ }
+
+ m_events->adoptHandler(m_events->forIDataSocket().connectionFailed(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleConnectionFailed));
+}
+
+void
+Client::setupConnection()
+{
+ assert(m_stream != NULL);
+
+ m_events->adoptHandler(m_events->forISocket().disconnected(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleDisconnected));
+ m_events->adoptHandler(m_events->forIStream().inputReady(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleHello));
+ m_events->adoptHandler(m_events->forIStream().outputError(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleOutputError));
+ m_events->adoptHandler(m_events->forIStream().inputShutdown(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleDisconnected));
+ m_events->adoptHandler(m_events->forIStream().outputShutdown(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleDisconnected));
+
+ m_events->adoptHandler(m_events->forISocket().stopRetry(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<Client>(this, &Client::handleStopRetry));
+}
+
+void
+Client::setupScreen()
+{
+ assert(m_server == NULL);
+
+ m_ready = false;
+ m_server = new ServerProxy(this, m_stream, m_events);
+ m_events->adoptHandler(m_events->forIScreen().shapeChanged(),
+ getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleShapeChanged));
+ m_events->adoptHandler(m_events->forClipboard().clipboardGrabbed(),
+ getEventTarget(),
+ new TMethodEventJob<Client>(this,
+ &Client::handleClipboardGrabbed));
+}
+
+void
+Client::setupTimer()
+{
+ assert(m_timer == NULL);
+
+ m_timer = m_events->newOneShotTimer(15.0, NULL);
+ m_events->adoptHandler(Event::kTimer, m_timer,
+ new TMethodEventJob<Client>(this,
+ &Client::handleConnectTimeout));
+}
+
+void
+Client::cleanupConnecting()
+{
+ if (m_stream != NULL) {
+ m_events->removeHandler(m_events->forIDataSocket().connected(),
+ m_stream->getEventTarget());
+ m_events->removeHandler(m_events->forIDataSocket().connectionFailed(),
+ m_stream->getEventTarget());
+ }
+}
+
+void
+Client::cleanupConnection()
+{
+ if (m_stream != NULL) {
+ m_events->removeHandler(m_events->forIStream().inputReady(),
+ m_stream->getEventTarget());
+ m_events->removeHandler(m_events->forIStream().outputError(),
+ m_stream->getEventTarget());
+ m_events->removeHandler(m_events->forIStream().inputShutdown(),
+ m_stream->getEventTarget());
+ m_events->removeHandler(m_events->forIStream().outputShutdown(),
+ m_stream->getEventTarget());
+ m_events->removeHandler(m_events->forISocket().disconnected(),
+ m_stream->getEventTarget());
+ m_events->removeHandler(m_events->forISocket().stopRetry(),
+ m_stream->getEventTarget());
+ cleanupStream();
+ }
+}
+
+void
+Client::cleanupScreen()
+{
+ if (m_server != NULL) {
+ if (m_ready) {
+ m_screen->disable();
+ m_ready = false;
+ }
+ m_events->removeHandler(m_events->forIScreen().shapeChanged(),
+ getEventTarget());
+ m_events->removeHandler(m_events->forClipboard().clipboardGrabbed(),
+ getEventTarget());
+ delete m_server;
+ m_server = NULL;
+ }
+}
+
+void
+Client::cleanupTimer()
+{
+ if (m_timer != NULL) {
+ m_events->removeHandler(Event::kTimer, m_timer);
+ m_events->deleteTimer(m_timer);
+ m_timer = NULL;
+ }
+}
+
+void
+Client::cleanupStream()
+{
+ delete m_stream;
+ m_stream = NULL;
+}
+
+void
+Client::handleConnected(const Event&, void*)
+{
+ LOG((CLOG_DEBUG1 "connected; wait for hello"));
+ cleanupConnecting();
+ setupConnection();
+
+ // reset clipboard state
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ m_ownClipboard[id] = false;
+ m_sentClipboard[id] = false;
+ m_timeClipboard[id] = 0;
+ }
+}
+
+void
+Client::handleConnectionFailed(const Event& event, void*)
+{
+ IDataSocket::ConnectionFailedInfo* info =
+ static_cast<IDataSocket::ConnectionFailedInfo*>(event.getData());
+
+ cleanupTimer();
+ cleanupConnecting();
+ cleanupStream();
+ LOG((CLOG_DEBUG1 "connection failed"));
+ sendConnectionFailedEvent(info->m_what.c_str());
+ delete info;
+}
+
+void
+Client::handleConnectTimeout(const Event&, void*)
+{
+ cleanupTimer();
+ cleanupConnecting();
+ cleanupConnection();
+ cleanupStream();
+ LOG((CLOG_DEBUG1 "connection timed out"));
+ sendConnectionFailedEvent("Timed out");
+}
+
+void
+Client::handleOutputError(const Event&, void*)
+{
+ cleanupTimer();
+ cleanupScreen();
+ cleanupConnection();
+ LOG((CLOG_WARN "error sending to server"));
+ sendEvent(m_events->forClient().disconnected(), NULL);
+}
+
+void
+Client::handleDisconnected(const Event&, void*)
+{
+ cleanupTimer();
+ cleanupScreen();
+ cleanupConnection();
+ LOG((CLOG_DEBUG1 "disconnected"));
+ sendEvent(m_events->forClient().disconnected(), NULL);
+}
+
+void
+Client::handleShapeChanged(const Event&, void*)
+{
+ LOG((CLOG_DEBUG "resolution changed"));
+ m_server->onInfoChanged();
+}
+
+void
+Client::handleClipboardGrabbed(const Event& event, void*)
+{
+ if (!m_enableClipboard) {
+ return;
+ }
+
+ const IScreen::ClipboardInfo* info =
+ static_cast<const IScreen::ClipboardInfo*>(event.getData());
+
+ // grab ownership
+ m_server->onGrabClipboard(info->m_id);
+
+ // we now own the clipboard and it has not been sent to the server
+ m_ownClipboard[info->m_id] = true;
+ m_sentClipboard[info->m_id] = false;
+ m_timeClipboard[info->m_id] = 0;
+
+ // if we're not the active screen then send the clipboard now,
+ // otherwise we'll wait until we leave.
+ if (!m_active) {
+ sendClipboard(info->m_id);
+ }
+}
+
+void
+Client::handleHello(const Event&, void*)
+{
+ SInt16 major, minor;
+ if (!ProtocolUtil::readf(m_stream, kMsgHello, &major, &minor)) {
+ sendConnectionFailedEvent("Protocol error from server, check encryption settings");
+ cleanupTimer();
+ cleanupConnection();
+ return;
+ }
+
+ // check versions
+ LOG((CLOG_DEBUG1 "got hello version %d.%d", major, minor));
+ if (major < kProtocolMajorVersion ||
+ (major == kProtocolMajorVersion && minor < kProtocolMinorVersion)) {
+ sendConnectionFailedEvent(XIncompatibleClient(major, minor).what());
+ cleanupTimer();
+ cleanupConnection();
+ return;
+ }
+
+ // say hello back
+ LOG((CLOG_DEBUG1 "say hello version %d.%d", kProtocolMajorVersion, kProtocolMinorVersion));
+ ProtocolUtil::writef(m_stream, kMsgHelloBack,
+ kProtocolMajorVersion,
+ kProtocolMinorVersion, &m_name);
+
+ // now connected but waiting to complete handshake
+ setupScreen();
+ cleanupTimer();
+
+ // make sure we process any remaining messages later. we won't
+ // receive another event for already pending messages so we fake
+ // one.
+ if (m_stream->isReady()) {
+ m_events->addEvent(Event(m_events->forIStream().inputReady(),
+ m_stream->getEventTarget()));
+ }
+}
+
+void
+Client::handleSuspend(const Event&, void*)
+{
+ LOG((CLOG_INFO "suspend"));
+ m_suspended = true;
+ bool wasConnected = isConnected();
+ disconnect(NULL);
+ m_connectOnResume = wasConnected;
+}
+
+void
+Client::handleResume(const Event&, void*)
+{
+ LOG((CLOG_INFO "resume"));
+ m_suspended = false;
+ if (m_connectOnResume) {
+ m_connectOnResume = false;
+ connect();
+ }
+}
+
+void
+Client::handleFileChunkSending(const Event& event, void*)
+{
+ sendFileChunk(event.getData());
+}
+
+void
+Client::handleFileRecieveCompleted(const Event& event, void*)
+{
+ onFileRecieveCompleted();
+}
+
+void
+Client::onFileRecieveCompleted()
+{
+ if (isReceivedFileSizeValid()) {
+ m_writeToDropDirThread = new Thread(
+ new TMethodJob<Client>(
+ this, &Client::writeToDropDirThread));
+ }
+}
+
+void
+Client::handleStopRetry(const Event&, void*)
+{
+ m_args.m_restartable = false;
+}
+
+void
+Client::writeToDropDirThread(void*)
+{
+ LOG((CLOG_DEBUG "starting write to drop dir thread"));
+
+ while (m_screen->isFakeDraggingStarted()) {
+ ARCH->sleep(.1f);
+ }
+
+ DropHelper::writeToDir(m_screen->getDropTarget(), m_dragFileList,
+ m_receivedFileData);
+}
+
+void
+Client::dragInfoReceived(UInt32 fileNum, String data)
+{
+ // TODO: fix duplicate function from CServer
+ if (!m_args.m_enableDragDrop) {
+ LOG((CLOG_DEBUG "drag drop not enabled, ignoring drag info."));
+ return;
+ }
+
+ DragInformation::parseDragInfo(m_dragFileList, fileNum, data);
+
+ m_screen->startDraggingFiles(m_dragFileList);
+}
+
+bool
+Client::isReceivedFileSizeValid()
+{
+ return m_expectedFileSize == m_receivedFileData.size();
+}
+
+void
+Client::sendFileToServer(const char* filename)
+{
+ if (m_sendFileThread != NULL) {
+ StreamChunker::interruptFile();
+ }
+
+ m_sendFileThread = new Thread(
+ new TMethodJob<Client>(
+ this, &Client::sendFileThread,
+ static_cast<void*>(const_cast<char*>(filename))));
+}
+
+void
+Client::sendFileThread(void* filename)
+{
+ try {
+ char* name = static_cast<char*>(filename);
+ StreamChunker::sendFile(name, m_events, this);
+ }
+ catch (std::runtime_error& error) {
+ LOG((CLOG_ERR "failed sending file chunks: %s", error.what()));
+ }
+
+ m_sendFileThread = NULL;
+}
+
+void
+Client::sendDragInfo(UInt32 fileCount, String& info, size_t size)
+{
+ m_server->sendDragInfo(fileCount, info.c_str(), size);
+}
diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h
new file mode 100644
index 0000000..7ac515d
--- /dev/null
+++ b/src/lib/client/Client.h
@@ -0,0 +1,227 @@
+/*
+ * barrier -- 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 "barrier/IClient.h"
+
+#include "barrier/Clipboard.h"
+#include "barrier/DragInformation.h"
+#include "barrier/INode.h"
+#include "barrier/ClientArgs.h"
+#include "net/NetworkAddress.h"
+#include "base/EventTypes.h"
+#include "mt/CondVar.h"
+
+class EventQueueTimer;
+namespace barrier { class Screen; }
+class ServerProxy;
+class IDataSocket;
+class ISocketFactory;
+namespace barrier { class IStream; }
+class IEventQueue;
+class Thread;
+class TCPSocket;
+
+//! Barrier client
+/*!
+This class implements the top-level client algorithms for barrier.
+*/
+class Client : public IClient, public INode {
+public:
+ class FailInfo {
+ public:
+ FailInfo(const char* what) : m_retry(false), m_what(what) { }
+ bool m_retry;
+ String m_what;
+ };
+
+public:
+ /*!
+ This client will attempt to connect to the server using \p name
+ as its name and \p address as the server's address and \p factory
+ to create the socket. \p screen is the local screen.
+ */
+ Client(IEventQueue* events, const String& name,
+ const NetworkAddress& address, ISocketFactory* socketFactory,
+ barrier::Screen* screen, ClientArgs const& args);
+
+ ~Client();
+
+ //! @name manipulators
+ //@{
+
+ //! Connect to server
+ /*!
+ Starts an attempt to connect to the server. This is ignored if
+ the client is trying to connect or is already connected.
+ */
+ void connect();
+
+ //! Disconnect
+ /*!
+ Disconnects from the server with an optional error message.
+ */
+ void disconnect(const char* msg);
+
+ //! Notify of handshake complete
+ /*!
+ Notifies the client that the connection handshake has completed.
+ */
+ virtual void handshakeComplete();
+
+ //! Received drag information
+ void dragInfoReceived(UInt32 fileNum, String data);
+
+ //! Create a new thread and use it to send file to Server
+ void sendFileToServer(const char* filename);
+
+ //! Send dragging file information back to server
+ void sendDragInfo(UInt32 fileCount, String& info, size_t size);
+
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test if connected
+ /*!
+ Returns true iff the client is successfully connected to the server.
+ */
+ bool isConnected() const;
+
+ //! Test if connecting
+ /*!
+ Returns true iff the client is currently attempting to connect to
+ the server.
+ */
+ bool isConnecting() const;
+
+ //! Get address of server
+ /*!
+ Returns the address of the server the client is connected (or wants
+ to connect) to.
+ */
+ NetworkAddress getServerAddress() const;
+
+ //! Return true if recieved file size is valid
+ bool isReceivedFileSizeValid();
+
+ //! Return expected file size
+ size_t& getExpectedFileSize() { return m_expectedFileSize; }
+
+ //! Return received file data
+ String& getReceivedFileData() { return m_receivedFileData; }
+
+ //! Return drag file list
+ DragFileList getDragFileList() { return m_dragFileList; }
+
+ //@}
+
+ // IScreen overrides
+ virtual void* getEventTarget() const;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const;
+
+ // IClient overrides
+ virtual void enter(SInt32 xAbs, SInt32 yAbs,
+ UInt32 seqNum, KeyModifierMask mask,
+ bool forScreensaver);
+ virtual bool leave();
+ virtual void setClipboard(ClipboardID, const IClipboard*);
+ virtual void grabClipboard(ClipboardID);
+ virtual void setClipboardDirty(ClipboardID, bool);
+ virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
+ virtual void keyRepeat(KeyID, KeyModifierMask,
+ SInt32 count, KeyButton);
+ virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
+ virtual void mouseDown(ButtonID);
+ virtual void mouseUp(ButtonID);
+ virtual void mouseMove(SInt32 xAbs, SInt32 yAbs);
+ virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel);
+ virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta);
+ virtual void screensaver(bool activate);
+ virtual void resetOptions();
+ virtual void setOptions(const OptionsList& options);
+ virtual String getName() const;
+
+private:
+ void sendClipboard(ClipboardID);
+ void sendEvent(Event::Type, void*);
+ void sendConnectionFailedEvent(const char* msg);
+ void sendFileChunk(const void* data);
+ void sendFileThread(void*);
+ void writeToDropDirThread(void*);
+ void setupConnecting();
+ void setupConnection();
+ void setupScreen();
+ void setupTimer();
+ void cleanupConnecting();
+ void cleanupConnection();
+ void cleanupScreen();
+ void cleanupTimer();
+ void cleanupStream();
+ void handleConnected(const Event&, void*);
+ void handleConnectionFailed(const Event&, void*);
+ void handleConnectTimeout(const Event&, void*);
+ void handleOutputError(const Event&, void*);
+ void handleDisconnected(const Event&, void*);
+ void handleShapeChanged(const Event&, void*);
+ void handleClipboardGrabbed(const Event&, void*);
+ void handleHello(const Event&, void*);
+ void handleSuspend(const Event& event, void*);
+ void handleResume(const Event& event, void*);
+ void handleFileChunkSending(const Event&, void*);
+ void handleFileRecieveCompleted(const Event&, void*);
+ void handleStopRetry(const Event&, void*);
+ void onFileRecieveCompleted();
+ void sendClipboardThread(void*);
+
+public:
+ bool m_mock;
+
+private:
+ String m_name;
+ NetworkAddress m_serverAddress;
+ ISocketFactory* m_socketFactory;
+ barrier::Screen* m_screen;
+ barrier::IStream* m_stream;
+ EventQueueTimer* m_timer;
+ ServerProxy* m_server;
+ bool m_ready;
+ bool m_active;
+ bool m_suspended;
+ bool m_connectOnResume;
+ bool m_ownClipboard[kClipboardEnd];
+ bool m_sentClipboard[kClipboardEnd];
+ IClipboard::Time m_timeClipboard[kClipboardEnd];
+ String m_dataClipboard[kClipboardEnd];
+ IEventQueue* m_events;
+ std::size_t m_expectedFileSize;
+ String m_receivedFileData;
+ DragFileList m_dragFileList;
+ String m_dragFileExt;
+ Thread* m_sendFileThread;
+ Thread* m_writeToDropDirThread;
+ TCPSocket* m_socket;
+ bool m_useSecureNetwork;
+ ClientArgs m_args;
+ bool m_enableClipboard;
+};
diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp
new file mode 100644
index 0000000..9224db6
--- /dev/null
+++ b/src/lib/client/ServerProxy.cpp
@@ -0,0 +1,908 @@
+/*
+ * barrier -- 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 "client/ServerProxy.h"
+
+#include "client/Client.h"
+#include "barrier/FileChunk.h"
+#include "barrier/ClipboardChunk.h"
+#include "barrier/StreamChunker.h"
+#include "barrier/Clipboard.h"
+#include "barrier/ProtocolUtil.h"
+#include "barrier/option_types.h"
+#include "barrier/protocol_types.h"
+#include "io/IStream.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+#include "base/XBase.h"
+
+#include <memory>
+
+//
+// ServerProxy
+//
+
+ServerProxy::ServerProxy(Client* client, barrier::IStream* stream, IEventQueue* events) :
+ m_client(client),
+ m_stream(stream),
+ m_seqNum(0),
+ m_compressMouse(false),
+ m_compressMouseRelative(false),
+ m_xMouse(0),
+ m_yMouse(0),
+ m_dxMouse(0),
+ m_dyMouse(0),
+ m_ignoreMouse(false),
+ m_keepAliveAlarm(0.0),
+ m_keepAliveAlarmTimer(NULL),
+ m_parser(&ServerProxy::parseHandshakeMessage),
+ m_events(events)
+{
+ assert(m_client != NULL);
+ assert(m_stream != NULL);
+
+ // initialize modifier translation table
+ for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id)
+ m_modifierTranslationTable[id] = id;
+
+ // handle data on stream
+ m_events->adoptHandler(m_events->forIStream().inputReady(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<ServerProxy>(this,
+ &ServerProxy::handleData));
+
+ m_events->adoptHandler(m_events->forClipboard().clipboardSending(),
+ this,
+ new TMethodEventJob<ServerProxy>(this,
+ &ServerProxy::handleClipboardSendingEvent));
+
+ // send heartbeat
+ setKeepAliveRate(kKeepAliveRate);
+}
+
+ServerProxy::~ServerProxy()
+{
+ setKeepAliveRate(-1.0);
+ m_events->removeHandler(m_events->forIStream().inputReady(),
+ m_stream->getEventTarget());
+}
+
+void
+ServerProxy::resetKeepAliveAlarm()
+{
+ if (m_keepAliveAlarmTimer != NULL) {
+ m_events->removeHandler(Event::kTimer, m_keepAliveAlarmTimer);
+ m_events->deleteTimer(m_keepAliveAlarmTimer);
+ m_keepAliveAlarmTimer = NULL;
+ }
+ if (m_keepAliveAlarm > 0.0) {
+ m_keepAliveAlarmTimer =
+ m_events->newOneShotTimer(m_keepAliveAlarm, NULL);
+ m_events->adoptHandler(Event::kTimer, m_keepAliveAlarmTimer,
+ new TMethodEventJob<ServerProxy>(this,
+ &ServerProxy::handleKeepAliveAlarm));
+ }
+}
+
+void
+ServerProxy::setKeepAliveRate(double rate)
+{
+ m_keepAliveAlarm = rate * kKeepAlivesUntilDeath;
+ resetKeepAliveAlarm();
+}
+
+void
+ServerProxy::handleData(const Event&, void*)
+{
+ // handle messages until there are no more. first read message code.
+ UInt8 code[4];
+ UInt32 n = m_stream->read(code, 4);
+ while (n != 0) {
+ // verify we got an entire code
+ if (n != 4) {
+ LOG((CLOG_ERR "incomplete message from server: %d bytes", n));
+ m_client->disconnect("incomplete message from server");
+ return;
+ }
+
+ // parse message
+ LOG((CLOG_DEBUG2 "msg from server: %c%c%c%c", code[0], code[1], code[2], code[3]));
+ switch ((this->*m_parser)(code)) {
+ case kOkay:
+ break;
+
+ case kUnknown:
+ LOG((CLOG_ERR "invalid message from server: %c%c%c%c", code[0], code[1], code[2], code[3]));
+ m_client->disconnect("invalid message from server");
+ return;
+
+ case kDisconnect:
+ return;
+ }
+
+ // next message
+ n = m_stream->read(code, 4);
+ }
+
+ flushCompressedMouse();
+}
+
+ServerProxy::EResult
+ServerProxy::parseHandshakeMessage(const UInt8* code)
+{
+ if (memcmp(code, kMsgQInfo, 4) == 0) {
+ queryInfo();
+ }
+
+ else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
+ infoAcknowledgment();
+ }
+
+ else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
+ setOptions();
+
+ // handshake is complete
+ m_parser = &ServerProxy::parseMessage;
+ m_client->handshakeComplete();
+ }
+
+ else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
+ resetOptions();
+ }
+
+ else if (memcmp(code, kMsgCKeepAlive, 4) == 0) {
+ // echo keep alives and reset alarm
+ ProtocolUtil::writef(m_stream, kMsgCKeepAlive);
+ resetKeepAliveAlarm();
+ }
+
+ else if (memcmp(code, kMsgCNoop, 4) == 0) {
+ // accept and discard no-op
+ }
+
+ else if (memcmp(code, kMsgCClose, 4) == 0) {
+ // server wants us to hangup
+ LOG((CLOG_DEBUG1 "recv close"));
+ m_client->disconnect(NULL);
+ return kDisconnect;
+ }
+
+ else if (memcmp(code, kMsgEIncompatible, 4) == 0) {
+ SInt32 major, minor;
+ ProtocolUtil::readf(m_stream,
+ kMsgEIncompatible + 4, &major, &minor);
+ LOG((CLOG_ERR "server has incompatible version %d.%d", major, minor));
+ m_client->disconnect("server has incompatible version");
+ return kDisconnect;
+ }
+
+ else if (memcmp(code, kMsgEBusy, 4) == 0) {
+ LOG((CLOG_ERR "server already has a connected client with name \"%s\"", m_client->getName().c_str()));
+ m_client->disconnect("server already has a connected client with our name");
+ return kDisconnect;
+ }
+
+ else if (memcmp(code, kMsgEUnknown, 4) == 0) {
+ LOG((CLOG_ERR "server refused client with name \"%s\"", m_client->getName().c_str()));
+ m_client->disconnect("server refused client with our name");
+ return kDisconnect;
+ }
+
+ else if (memcmp(code, kMsgEBad, 4) == 0) {
+ LOG((CLOG_ERR "server disconnected due to a protocol error"));
+ m_client->disconnect("server reported a protocol error");
+ return kDisconnect;
+ }
+ else {
+ return kUnknown;
+ }
+
+ return kOkay;
+}
+
+ServerProxy::EResult
+ServerProxy::parseMessage(const UInt8* code)
+{
+ if (memcmp(code, kMsgDMouseMove, 4) == 0) {
+ mouseMove();
+ }
+
+ else if (memcmp(code, kMsgDMouseRelMove, 4) == 0) {
+ mouseRelativeMove();
+ }
+
+ else if (memcmp(code, kMsgDMouseWheel, 4) == 0) {
+ mouseWheel();
+ }
+
+ else if (memcmp(code, kMsgDKeyDown, 4) == 0) {
+ keyDown();
+ }
+
+ else if (memcmp(code, kMsgDKeyUp, 4) == 0) {
+ keyUp();
+ }
+
+ else if (memcmp(code, kMsgDMouseDown, 4) == 0) {
+ mouseDown();
+ }
+
+ else if (memcmp(code, kMsgDMouseUp, 4) == 0) {
+ mouseUp();
+ }
+
+ else if (memcmp(code, kMsgDKeyRepeat, 4) == 0) {
+ keyRepeat();
+ }
+
+ else if (memcmp(code, kMsgCKeepAlive, 4) == 0) {
+ // echo keep alives and reset alarm
+ ProtocolUtil::writef(m_stream, kMsgCKeepAlive);
+ resetKeepAliveAlarm();
+ }
+
+ else if (memcmp(code, kMsgCNoop, 4) == 0) {
+ // accept and discard no-op
+ }
+
+ else if (memcmp(code, kMsgCEnter, 4) == 0) {
+ enter();
+ }
+
+ else if (memcmp(code, kMsgCLeave, 4) == 0) {
+ leave();
+ }
+
+ else if (memcmp(code, kMsgCClipboard, 4) == 0) {
+ grabClipboard();
+ }
+
+ else if (memcmp(code, kMsgCScreenSaver, 4) == 0) {
+ screensaver();
+ }
+
+ else if (memcmp(code, kMsgQInfo, 4) == 0) {
+ queryInfo();
+ }
+
+ else if (memcmp(code, kMsgCInfoAck, 4) == 0) {
+ infoAcknowledgment();
+ }
+
+ else if (memcmp(code, kMsgDClipboard, 4) == 0) {
+ setClipboard();
+ }
+
+ else if (memcmp(code, kMsgCResetOptions, 4) == 0) {
+ resetOptions();
+ }
+
+ else if (memcmp(code, kMsgDSetOptions, 4) == 0) {
+ setOptions();
+ }
+
+ else if (memcmp(code, kMsgDFileTransfer, 4) == 0) {
+ fileChunkReceived();
+ }
+ else if (memcmp(code, kMsgDDragInfo, 4) == 0) {
+ dragInfoReceived();
+ }
+
+ else if (memcmp(code, kMsgCClose, 4) == 0) {
+ // server wants us to hangup
+ LOG((CLOG_DEBUG1 "recv close"));
+ m_client->disconnect(NULL);
+ return kDisconnect;
+ }
+ else if (memcmp(code, kMsgEBad, 4) == 0) {
+ LOG((CLOG_ERR "server disconnected due to a protocol error"));
+ m_client->disconnect("server reported a protocol error");
+ return kDisconnect;
+ }
+ else {
+ return kUnknown;
+ }
+
+ // send a reply. this is intended to work around a delay when
+ // running a linux server and an OS X (any BSD?) client. the
+ // client waits to send an ACK (if the system control flag
+ // net.inet.tcp.delayed_ack is 1) in hopes of piggybacking it
+ // on a data packet. we provide that packet here. i don't
+ // know why a delayed ACK should cause the server to wait since
+ // TCP_NODELAY is enabled.
+ ProtocolUtil::writef(m_stream, kMsgCNoop);
+
+ return kOkay;
+}
+
+void
+ServerProxy::handleKeepAliveAlarm(const Event&, void*)
+{
+ LOG((CLOG_NOTE "server is dead"));
+ m_client->disconnect("server is not responding");
+}
+
+void
+ServerProxy::onInfoChanged()
+{
+ // ignore mouse motion until we receive acknowledgment of our info
+ // change message.
+ m_ignoreMouse = true;
+
+ // send info update
+ queryInfo();
+}
+
+bool
+ServerProxy::onGrabClipboard(ClipboardID id)
+{
+ LOG((CLOG_DEBUG1 "sending clipboard %d changed", id));
+ ProtocolUtil::writef(m_stream, kMsgCClipboard, id, m_seqNum);
+ return true;
+}
+
+void
+ServerProxy::onClipboardChanged(ClipboardID id, const IClipboard* clipboard)
+{
+ String data = IClipboard::marshall(clipboard);
+ LOG((CLOG_DEBUG "sending clipboard %d seqnum=%d", id, m_seqNum));
+
+ StreamChunker::sendClipboard(data, data.size(), id, m_seqNum, m_events, this);
+}
+
+void
+ServerProxy::flushCompressedMouse()
+{
+ if (m_compressMouse) {
+ m_compressMouse = false;
+ m_client->mouseMove(m_xMouse, m_yMouse);
+ }
+ if (m_compressMouseRelative) {
+ m_compressMouseRelative = false;
+ m_client->mouseRelativeMove(m_dxMouse, m_dyMouse);
+ m_dxMouse = 0;
+ m_dyMouse = 0;
+ }
+}
+
+void
+ServerProxy::sendInfo(const ClientInfo& info)
+{
+ LOG((CLOG_DEBUG1 "sending info shape=%d,%d %dx%d", info.m_x, info.m_y, info.m_w, info.m_h));
+ ProtocolUtil::writef(m_stream, kMsgDInfo,
+ info.m_x, info.m_y,
+ info.m_w, info.m_h, 0,
+ info.m_mx, info.m_my);
+}
+
+KeyID
+ServerProxy::translateKey(KeyID id) const
+{
+ static const KeyID s_translationTable[kKeyModifierIDLast][2] = {
+ { kKeyNone, kKeyNone },
+ { kKeyShift_L, kKeyShift_R },
+ { kKeyControl_L, kKeyControl_R },
+ { kKeyAlt_L, kKeyAlt_R },
+ { kKeyMeta_L, kKeyMeta_R },
+ { kKeySuper_L, kKeySuper_R },
+ { kKeyAltGr, kKeyAltGr}
+ };
+
+ KeyModifierID id2 = kKeyModifierIDNull;
+ UInt32 side = 0;
+ switch (id) {
+ case kKeyShift_L:
+ id2 = kKeyModifierIDShift;
+ side = 0;
+ break;
+
+ case kKeyShift_R:
+ id2 = kKeyModifierIDShift;
+ side = 1;
+ break;
+
+ case kKeyControl_L:
+ id2 = kKeyModifierIDControl;
+ side = 0;
+ break;
+
+ case kKeyControl_R:
+ id2 = kKeyModifierIDControl;
+ side = 1;
+ break;
+
+ case kKeyAlt_L:
+ id2 = kKeyModifierIDAlt;
+ side = 0;
+ break;
+
+ case kKeyAlt_R:
+ id2 = kKeyModifierIDAlt;
+ side = 1;
+ break;
+
+ case kKeyAltGr:
+ id2 = kKeyModifierIDAltGr;
+ side = 1; // there is only one alt gr key on the right side
+ break;
+
+ case kKeyMeta_L:
+ id2 = kKeyModifierIDMeta;
+ side = 0;
+ break;
+
+ case kKeyMeta_R:
+ id2 = kKeyModifierIDMeta;
+ side = 1;
+ break;
+
+ case kKeySuper_L:
+ id2 = kKeyModifierIDSuper;
+ side = 0;
+ break;
+
+ case kKeySuper_R:
+ id2 = kKeyModifierIDSuper;
+ side = 1;
+ break;
+ }
+
+ if (id2 != kKeyModifierIDNull) {
+ return s_translationTable[m_modifierTranslationTable[id2]][side];
+ }
+ else {
+ return id;
+ }
+}
+
+KeyModifierMask
+ServerProxy::translateModifierMask(KeyModifierMask mask) const
+{
+ static const KeyModifierMask s_masks[kKeyModifierIDLast] = {
+ 0x0000,
+ KeyModifierShift,
+ KeyModifierControl,
+ KeyModifierAlt,
+ KeyModifierMeta,
+ KeyModifierSuper,
+ KeyModifierAltGr
+ };
+
+ KeyModifierMask newMask = mask & ~(KeyModifierShift |
+ KeyModifierControl |
+ KeyModifierAlt |
+ KeyModifierMeta |
+ KeyModifierSuper |
+ KeyModifierAltGr );
+ if ((mask & KeyModifierShift) != 0) {
+ newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDShift]];
+ }
+ if ((mask & KeyModifierControl) != 0) {
+ newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDControl]];
+ }
+ if ((mask & KeyModifierAlt) != 0) {
+ newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDAlt]];
+ }
+ if ((mask & KeyModifierAltGr) != 0) {
+ newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDAltGr]];
+ }
+ if ((mask & KeyModifierMeta) != 0) {
+ newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDMeta]];
+ }
+ if ((mask & KeyModifierSuper) != 0) {
+ newMask |= s_masks[m_modifierTranslationTable[kKeyModifierIDSuper]];
+ }
+ return newMask;
+}
+
+void
+ServerProxy::enter()
+{
+ // parse
+ SInt16 x, y;
+ UInt16 mask;
+ UInt32 seqNum;
+ ProtocolUtil::readf(m_stream, kMsgCEnter + 4, &x, &y, &seqNum, &mask);
+ 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;
+
+ // forward
+ m_client->enter(x, y, seqNum, static_cast<KeyModifierMask>(mask), false);
+}
+
+void
+ServerProxy::leave()
+{
+ // parse
+ LOG((CLOG_DEBUG1 "recv leave"));
+
+ // send last mouse motion
+ flushCompressedMouse();
+
+ // forward
+ m_client->leave();
+}
+
+void
+ServerProxy::setClipboard()
+{
+ // parse
+ static String dataCached;
+ ClipboardID id;
+ UInt32 seq;
+
+ int r = ClipboardChunk::assemble(m_stream, dataCached, id, seq);
+
+ if (r == kStart) {
+ size_t size = ClipboardChunk::getExpectedSize();
+ LOG((CLOG_DEBUG "receiving clipboard %d size=%d", id, size));
+ }
+ else if (r == kFinish) {
+ LOG((CLOG_DEBUG "received clipboard %d size=%d", id, dataCached.size()));
+
+ // forward
+ Clipboard clipboard;
+ clipboard.unmarshall(dataCached, 0);
+ m_client->setClipboard(id, &clipboard);
+
+ LOG((CLOG_INFO "clipboard was updated"));
+ }
+}
+
+void
+ServerProxy::grabClipboard()
+{
+ // parse
+ ClipboardID id;
+ UInt32 seqNum;
+ ProtocolUtil::readf(m_stream, kMsgCClipboard + 4, &id, &seqNum);
+ LOG((CLOG_DEBUG "recv grab clipboard %d", id));
+
+ // validate
+ if (id >= kClipboardEnd) {
+ return;
+ }
+
+ // forward
+ m_client->grabClipboard(id);
+}
+
+void
+ServerProxy::keyDown()
+{
+ // get mouse up to date
+ flushCompressedMouse();
+
+ // 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));
+
+ // translate
+ KeyID id2 = translateKey(static_cast<KeyID>(id));
+ KeyModifierMask mask2 = translateModifierMask(
+ static_cast<KeyModifierMask>(mask));
+ if (id2 != static_cast<KeyID>(id) ||
+ mask2 != static_cast<KeyModifierMask>(mask))
+ LOG((CLOG_DEBUG1 "key down translated to id=0x%08x, mask=0x%04x", id2, mask2));
+
+ // forward
+ m_client->keyDown(id2, mask2, button);
+}
+
+void
+ServerProxy::keyRepeat()
+{
+ // get mouse up to date
+ flushCompressedMouse();
+
+ // parse
+ UInt16 id, mask, count, button;
+ 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));
+
+ // translate
+ KeyID id2 = translateKey(static_cast<KeyID>(id));
+ KeyModifierMask mask2 = translateModifierMask(
+ static_cast<KeyModifierMask>(mask));
+ if (id2 != static_cast<KeyID>(id) ||
+ mask2 != static_cast<KeyModifierMask>(mask))
+ LOG((CLOG_DEBUG1 "key repeat translated to id=0x%08x, mask=0x%04x", id2, mask2));
+
+ // forward
+ m_client->keyRepeat(id2, mask2, count, button);
+}
+
+void
+ServerProxy::keyUp()
+{
+ // get mouse up to date
+ flushCompressedMouse();
+
+ // parse
+ UInt16 id, mask, button;
+ ProtocolUtil::readf(m_stream, kMsgDKeyUp + 4, &id, &mask, &button);
+ LOG((CLOG_DEBUG1 "recv key up id=0x%08x, mask=0x%04x, button=0x%04x", id, mask, button));
+
+ // translate
+ KeyID id2 = translateKey(static_cast<KeyID>(id));
+ KeyModifierMask mask2 = translateModifierMask(
+ static_cast<KeyModifierMask>(mask));
+ if (id2 != static_cast<KeyID>(id) ||
+ mask2 != static_cast<KeyModifierMask>(mask))
+ LOG((CLOG_DEBUG1 "key up translated to id=0x%08x, mask=0x%04x", id2, mask2));
+
+ // forward
+ m_client->keyUp(id2, mask2, button);
+}
+
+void
+ServerProxy::mouseDown()
+{
+ // get mouse up to date
+ flushCompressedMouse();
+
+ // parse
+ SInt8 id;
+ ProtocolUtil::readf(m_stream, kMsgDMouseDown + 4, &id);
+ LOG((CLOG_DEBUG1 "recv mouse down id=%d", id));
+
+ // forward
+ m_client->mouseDown(static_cast<ButtonID>(id));
+}
+
+void
+ServerProxy::mouseUp()
+{
+ // get mouse up to date
+ flushCompressedMouse();
+
+ // parse
+ SInt8 id;
+ ProtocolUtil::readf(m_stream, kMsgDMouseUp + 4, &id);
+ LOG((CLOG_DEBUG1 "recv mouse up id=%d", id));
+
+ // forward
+ m_client->mouseUp(static_cast<ButtonID>(id));
+}
+
+void
+ServerProxy::mouseMove()
+{
+ // parse
+ bool ignore;
+ SInt16 x, y;
+ ProtocolUtil::readf(m_stream, kMsgDMouseMove + 4, &x, &y);
+
+ // note if we should ignore the move
+ ignore = m_ignoreMouse;
+
+ // compress mouse motion events if more input follows
+ if (!ignore && !m_compressMouse && m_stream->isReady()) {
+ m_compressMouse = true;
+ }
+
+ // if compressing then ignore the motion but record it
+ if (m_compressMouse) {
+ m_compressMouseRelative = false;
+ ignore = true;
+ m_xMouse = x;
+ m_yMouse = y;
+ m_dxMouse = 0;
+ m_dyMouse = 0;
+ }
+ LOG((CLOG_DEBUG2 "recv mouse move %d,%d", x, y));
+
+ // forward
+ if (!ignore) {
+ m_client->mouseMove(x, y);
+ }
+}
+
+void
+ServerProxy::mouseRelativeMove()
+{
+ // parse
+ bool ignore;
+ SInt16 dx, dy;
+ ProtocolUtil::readf(m_stream, kMsgDMouseRelMove + 4, &dx, &dy);
+
+ // note if we should ignore the move
+ ignore = m_ignoreMouse;
+
+ // compress mouse motion events if more input follows
+ if (!ignore && !m_compressMouseRelative && m_stream->isReady()) {
+ m_compressMouseRelative = true;
+ }
+
+ // if compressing then ignore the motion but record it
+ if (m_compressMouseRelative) {
+ ignore = true;
+ m_dxMouse += dx;
+ m_dyMouse += dy;
+ }
+ LOG((CLOG_DEBUG2 "recv mouse relative move %d,%d", dx, dy));
+
+ // forward
+ if (!ignore) {
+ m_client->mouseRelativeMove(dx, dy);
+ }
+}
+
+void
+ServerProxy::mouseWheel()
+{
+ // get mouse up to date
+ flushCompressedMouse();
+
+ // parse
+ SInt16 xDelta, yDelta;
+ ProtocolUtil::readf(m_stream, kMsgDMouseWheel + 4, &xDelta, &yDelta);
+ LOG((CLOG_DEBUG2 "recv mouse wheel %+d,%+d", xDelta, yDelta));
+
+ // forward
+ m_client->mouseWheel(xDelta, yDelta);
+}
+
+void
+ServerProxy::screensaver()
+{
+ // parse
+ SInt8 on;
+ ProtocolUtil::readf(m_stream, kMsgCScreenSaver + 4, &on);
+ LOG((CLOG_DEBUG1 "recv screen saver on=%d", on));
+
+ // forward
+ m_client->screensaver(on != 0);
+}
+
+void
+ServerProxy::resetOptions()
+{
+ // parse
+ LOG((CLOG_DEBUG1 "recv reset options"));
+
+ // forward
+ m_client->resetOptions();
+
+ // reset keep alive
+ setKeepAliveRate(kKeepAliveRate);
+
+ // reset modifier translation table
+ for (KeyModifierID id = 0; id < kKeyModifierIDLast; ++id) {
+ m_modifierTranslationTable[id] = id;
+ }
+}
+
+void
+ServerProxy::setOptions()
+{
+ // parse
+ OptionsList options;
+ ProtocolUtil::readf(m_stream, kMsgDSetOptions + 4, &options);
+ LOG((CLOG_DEBUG1 "recv set options size=%d", options.size()));
+
+ // forward
+ m_client->setOptions(options);
+
+ // update modifier table
+ for (UInt32 i = 0, n = (UInt32)options.size(); i < n; i += 2) {
+ KeyModifierID id = kKeyModifierIDNull;
+ if (options[i] == kOptionModifierMapForShift) {
+ id = kKeyModifierIDShift;
+ }
+ else if (options[i] == kOptionModifierMapForControl) {
+ id = kKeyModifierIDControl;
+ }
+ else if (options[i] == kOptionModifierMapForAlt) {
+ id = kKeyModifierIDAlt;
+ }
+ else if (options[i] == kOptionModifierMapForAltGr) {
+ id = kKeyModifierIDAltGr;
+ }
+ else if (options[i] == kOptionModifierMapForMeta) {
+ id = kKeyModifierIDMeta;
+ }
+ else if (options[i] == kOptionModifierMapForSuper) {
+ id = kKeyModifierIDSuper;
+ }
+ else if (options[i] == kOptionHeartbeat) {
+ // update keep alive
+ setKeepAliveRate(1.0e-3 * static_cast<double>(options[i + 1]));
+ }
+
+ if (id != kKeyModifierIDNull) {
+ m_modifierTranslationTable[id] =
+ static_cast<KeyModifierID>(options[i + 1]);
+ LOG((CLOG_DEBUG1 "modifier %d mapped to %d", id, m_modifierTranslationTable[id]));
+ }
+ }
+}
+
+void
+ServerProxy::queryInfo()
+{
+ ClientInfo info;
+ m_client->getShape(info.m_x, info.m_y, info.m_w, info.m_h);
+ m_client->getCursorPos(info.m_mx, info.m_my);
+ sendInfo(info);
+}
+
+void
+ServerProxy::infoAcknowledgment()
+{
+ LOG((CLOG_DEBUG1 "recv info acknowledgment"));
+ m_ignoreMouse = false;
+}
+
+void
+ServerProxy::fileChunkReceived()
+{
+ int result = FileChunk::assemble(
+ m_stream,
+ m_client->getReceivedFileData(),
+ m_client->getExpectedFileSize());
+
+ if (result == kFinish) {
+ m_events->addEvent(Event(m_events->forFile().fileRecieveCompleted(), m_client));
+ }
+ else if (result == kStart) {
+ if (m_client->getDragFileList().size() > 0) {
+ String filename = m_client->getDragFileList().at(0).getFilename();
+ LOG((CLOG_DEBUG "start receiving %s", filename.c_str()));
+ }
+ }
+}
+
+void
+ServerProxy::dragInfoReceived()
+{
+ // parse
+ UInt32 fileNum = 0;
+ String content;
+ ProtocolUtil::readf(m_stream, kMsgDDragInfo + 4, &fileNum, &content);
+
+ m_client->dragInfoReceived(fileNum, content);
+}
+
+void
+ServerProxy::handleClipboardSendingEvent(const Event& event, void*)
+{
+ ClipboardChunk::send(m_stream, event.getData());
+}
+
+void
+ServerProxy::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
+{
+ FileChunk::send(m_stream, mark, data, dataSize);
+}
+
+void
+ServerProxy::sendDragInfo(UInt32 fileCount, const char* info, size_t size)
+{
+ String data(info, size);
+ ProtocolUtil::writef(m_stream, kMsgDDragInfo, fileCount, &data);
+}
diff --git a/src/lib/client/ServerProxy.h b/src/lib/client/ServerProxy.h
new file mode 100644
index 0000000..2ad711a
--- /dev/null
+++ b/src/lib/client/ServerProxy.h
@@ -0,0 +1,133 @@
+/*
+ * barrier -- 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 "barrier/clipboard_types.h"
+#include "barrier/key_types.h"
+#include "base/Event.h"
+#include "base/Stopwatch.h"
+#include "base/String.h"
+
+class Client;
+class ClientInfo;
+class EventQueueTimer;
+class IClipboard;
+namespace barrier { class IStream; }
+class IEventQueue;
+
+//! Proxy for server
+/*!
+This class acts a proxy for the server, converting calls into messages
+to the server and messages from the server to calls on the client.
+*/
+class ServerProxy {
+public:
+ /*!
+ Process messages from the server on \p stream and forward to
+ \p client.
+ */
+ ServerProxy(Client* client, barrier::IStream* stream, IEventQueue* events);
+ ~ServerProxy();
+
+ //! @name manipulators
+ //@{
+
+ void onInfoChanged();
+ bool onGrabClipboard(ClipboardID);
+ void onClipboardChanged(ClipboardID, const IClipboard*);
+
+ //@}
+
+ // sending file chunk to server
+ void fileChunkSending(UInt8 mark, char* data, size_t dataSize);
+
+ // sending dragging information to server
+ void sendDragInfo(UInt32 fileCount, const char* info, size_t size);
+
+#ifdef TEST_ENV
+ void handleDataForTest() { handleData(Event(), NULL); }
+#endif
+
+protected:
+ enum EResult { kOkay, kUnknown, kDisconnect };
+ EResult parseHandshakeMessage(const UInt8* code);
+ EResult parseMessage(const UInt8* code);
+
+private:
+ // if compressing mouse motion then send the last motion now
+ void flushCompressedMouse();
+
+ void sendInfo(const ClientInfo&);
+
+ void resetKeepAliveAlarm();
+ void setKeepAliveRate(double);
+
+ // modifier key translation
+ KeyID translateKey(KeyID) const;
+ KeyModifierMask translateModifierMask(KeyModifierMask) const;
+
+ // event handlers
+ void handleData(const Event&, void*);
+ void handleKeepAliveAlarm(const Event&, void*);
+
+ // message handlers
+ void enter();
+ void leave();
+ void setClipboard();
+ void grabClipboard();
+ void keyDown();
+ void keyRepeat();
+ void keyUp();
+ void mouseDown();
+ void mouseUp();
+ void mouseMove();
+ void mouseRelativeMove();
+ void mouseWheel();
+ void screensaver();
+ void resetOptions();
+ void setOptions();
+ void queryInfo();
+ void infoAcknowledgment();
+ void fileChunkReceived();
+ void dragInfoReceived();
+ void handleClipboardSendingEvent(const Event&, void*);
+
+private:
+ typedef EResult (ServerProxy::*MessageParser)(const UInt8*);
+
+ Client* m_client;
+ barrier::IStream* m_stream;
+
+ UInt32 m_seqNum;
+
+ bool m_compressMouse;
+ bool m_compressMouseRelative;
+ SInt32 m_xMouse, m_yMouse;
+ SInt32 m_dxMouse, m_dyMouse;
+
+ bool m_ignoreMouse;
+
+ KeyModifierID m_modifierTranslationTable[kKeyModifierIDLast];
+
+ double m_keepAliveAlarm;
+ EventQueueTimer* m_keepAliveAlarmTimer;
+
+ MessageParser m_parser;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/common/CMakeLists.txt b/src/lib/common/CMakeLists.txt
new file mode 100644
index 0000000..56d9fc9
--- /dev/null
+++ b/src/lib/common/CMakeLists.txt
@@ -0,0 +1,24 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(common STATIC ${sources})
diff --git a/src/lib/common/IInterface.h b/src/lib/common/IInterface.h
new file mode 100644
index 0000000..84a76a9
--- /dev/null
+++ b/src/lib/common/IInterface.h
@@ -0,0 +1,32 @@
+/*
+ * barrier -- 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 "common/common.h"
+
+//! Base class of interfaces
+/*!
+This is the base class of all interface classes. An interface class has
+only pure virtual methods.
+*/
+class IInterface {
+public:
+ //! Interface destructor does nothing
+ virtual ~IInterface() { }
+};
diff --git a/src/lib/common/MacOSXPrecomp.h b/src/lib/common/MacOSXPrecomp.h
new file mode 100644
index 0000000..7dbc8d0
--- /dev/null
+++ b/src/lib/common/MacOSXPrecomp.h
@@ -0,0 +1,23 @@
+/*
+ * barrier -- 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/>.
+ */
+
+ //
+// Prefix header for all source files of the 'deleteme' target in the 'deleteme' project.
+//
+
+#include <Carbon/Carbon.h>
diff --git a/src/lib/common/Version.cpp b/src/lib/common/Version.cpp
new file mode 100644
index 0000000..94d6c5d
--- /dev/null
+++ b/src/lib/common/Version.cpp
@@ -0,0 +1,29 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "common/Version.h"
+
+const char* kApplication = "Barrier";
+const char* kCopyright = "Copyright (C) 2018 Debauchee Open Source Group\n"
+ "Copyright (C) 2012-2016 Symless Ltd.\n"
+ "Copyright (C) 2008-2014 Nick Bolton\n"
+ "Copyright (C) 2002-2014 Chris Schoeneman";
+const char* kContact = "Email: todo@mail.com";
+const char* kWebsite = "https://github.com/debauchee/barrier/";
+const char* kVersion = BARRIER_VERSION;
+const char* kAppVersion = "Barrier " BARRIER_VERSION;
diff --git a/src/lib/common/Version.h b/src/lib/common/Version.h
new file mode 100644
index 0000000..66bb2e2
--- /dev/null
+++ b/src/lib/common/Version.h
@@ -0,0 +1,39 @@
+/*
+ * barrier -- 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 "common/common.h"
+
+// set version macro if not set yet
+#if !defined(BARRIER_VERSION)
+#error Version was not set (should be passed to compiler).
+#endif
+
+// important strings
+extern const char* kApplication;
+extern const char* kCopyright;
+extern const char* kContact;
+extern const char* kWebsite;
+
+// build version. follows linux kernel style: an even minor number implies
+// a release version, odd implies development version.
+extern const char* kVersion;
+
+// application version
+extern const char* kAppVersion;
diff --git a/src/lib/common/basic_types.h b/src/lib/common/basic_types.h
new file mode 100644
index 0000000..f84550c
--- /dev/null
+++ b/src/lib/common/basic_types.h
@@ -0,0 +1,92 @@
+/*
+ * barrier -- 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 "common/common.h"
+
+//
+// pick types of particular sizes
+//
+
+#if !defined(TYPE_OF_SIZE_1)
+# if SIZEOF_CHAR == 1
+# define TYPE_OF_SIZE_1 char
+# endif
+#endif
+
+#if !defined(TYPE_OF_SIZE_2)
+# if SIZEOF_INT == 2
+# define TYPE_OF_SIZE_2 int
+# else
+# define TYPE_OF_SIZE_2 short
+# endif
+#endif
+
+#if !defined(TYPE_OF_SIZE_4)
+ // Carbon defines SInt32 and UInt32 in terms of long
+# if SIZEOF_INT == 4 && !defined(__APPLE__)
+# define TYPE_OF_SIZE_4 int
+# else
+# define TYPE_OF_SIZE_4 long
+# endif
+#endif
+
+ //
+// verify existence of required types
+//
+
+#if !defined(TYPE_OF_SIZE_1)
+# error No 1 byte integer type
+#endif
+#if !defined(TYPE_OF_SIZE_2)
+# error No 2 byte integer type
+#endif
+#if !defined(TYPE_OF_SIZE_4)
+# error No 4 byte integer type
+#endif
+
+
+//
+// make typedefs
+//
+// except for SInt8 and UInt8 these types are only guaranteed to be
+// at least as big as indicated (in bits). that is, they may be
+// larger than indicated.
+//
+
+// Added this because it doesn't compile on OS X 10.6 because they are already defined in Carbon
+#if !defined(__MACTYPES__)
+#if defined(__APPLE__)
+#include <CoreServices/CoreServices.h>
+#else
+typedef signed TYPE_OF_SIZE_1 SInt8;
+typedef signed TYPE_OF_SIZE_2 SInt16;
+typedef signed TYPE_OF_SIZE_4 SInt32;
+typedef unsigned TYPE_OF_SIZE_1 UInt8;
+typedef unsigned TYPE_OF_SIZE_2 UInt16;
+typedef unsigned TYPE_OF_SIZE_4 UInt32;
+#endif
+#endif
+//
+// clean up
+//
+
+#undef TYPE_OF_SIZE_1
+#undef TYPE_OF_SIZE_2
+#undef TYPE_OF_SIZE_4
diff --git a/src/lib/common/common.h b/src/lib/common/common.h
new file mode 100644
index 0000000..5ea215f
--- /dev/null
+++ b/src/lib/common/common.h
@@ -0,0 +1,58 @@
+/*
+ * barrier -- 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
+
+#if defined(_WIN32)
+# define SYSAPI_WIN32 1
+# define WINAPI_MSWINDOWS 1
+#elif HAVE_CONFIG_H
+# include "config.h"
+#else
+# error "config.h missing"
+#endif
+
+// VC++ has built-in sized types
+#if defined(_MSC_VER)
+# include <wchar.h>
+# define TYPE_OF_SIZE_1 __int8
+# define TYPE_OF_SIZE_2 __int16
+# define TYPE_OF_SIZE_4 __int32
+#else
+# define SIZE_OF_CHAR 1
+# define SIZE_OF_SHORT 2
+# define SIZE_OF_INT 4
+# define SIZE_OF_LONG 4
+#endif
+
+// define NULL
+#include <stddef.h>
+
+// make assert available since we use it a lot
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+enum {
+ kExitSuccess = 0, // successful completion
+ kExitFailed = 1, // general failure
+ kExitTerminated = 2, // killed by signal
+ kExitArgs = 3, // bad arguments
+ kExitConfig = 4, // cannot read configuration
+ kExitSubscription = 5 // subscription error
+};
diff --git a/src/lib/common/stdbitset.h b/src/lib/common/stdbitset.h
new file mode 100644
index 0000000..1096249
--- /dev/null
+++ b/src/lib/common/stdbitset.h
@@ -0,0 +1,21 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+#include <bitset>
+#include "common/stdpost.h"
diff --git a/src/lib/common/stddeque.h b/src/lib/common/stddeque.h
new file mode 100644
index 0000000..ffaed24
--- /dev/null
+++ b/src/lib/common/stddeque.h
@@ -0,0 +1,21 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+#include <deque>
+#include "common/stdpost.h"
diff --git a/src/lib/common/stdexcept.h b/src/lib/common/stdexcept.h
new file mode 100644
index 0000000..2bd96b3
--- /dev/null
+++ b/src/lib/common/stdexcept.h
@@ -0,0 +1,23 @@
+/*
+ * barrier -- 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 <stdexcept>
+
+// apple declares _NOEXCEPT
+#ifndef _NOEXCEPT
+# define _NOEXCEPT throw()
+#endif
diff --git a/src/lib/common/stdfstream.h b/src/lib/common/stdfstream.h
new file mode 100644
index 0000000..e9aa263
--- /dev/null
+++ b/src/lib/common/stdfstream.h
@@ -0,0 +1,22 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+#include <fstream>
+#include "common/stdpost.h"
+#include "common/stdistream.h"
diff --git a/src/lib/common/stdistream.h b/src/lib/common/stdistream.h
new file mode 100644
index 0000000..b19e2ab
--- /dev/null
+++ b/src/lib/common/stdistream.h
@@ -0,0 +1,47 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+#if HAVE_ISTREAM
+#include <istream>
+#else
+#include <iostream>
+#endif
+#include "common/stdpost.h"
+
+#if defined(_MSC_VER) && _MSC_VER <= 1200
+// VC++6 istream has no overloads for __int* types, .NET does
+inline
+std::istream& operator>>(std::istream& s, SInt8& i)
+{ return s >> (signed char&)i; }
+inline
+std::istream& operator>>(std::istream& s, SInt16& i)
+{ return s >> (short&)i; }
+inline
+std::istream& operator>>(std::istream& s, SInt32& i)
+{ return s >> (int&)i; }
+inline
+std::istream& operator>>(std::istream& s, UInt8& i)
+{ return s >> (unsigned char&)i; }
+inline
+std::istream& operator>>(std::istream& s, UInt16& i)
+{ return s >> (unsigned short&)i; }
+inline
+std::istream& operator>>(std::istream& s, UInt32& i)
+{ return s >> (unsigned int&)i; }
+#endif
diff --git a/src/lib/common/stdlist.h b/src/lib/common/stdlist.h
new file mode 100644
index 0000000..d530e57
--- /dev/null
+++ b/src/lib/common/stdlist.h
@@ -0,0 +1,21 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+#include <list>
+#include "common/stdpost.h"
diff --git a/src/lib/common/stdmap.h b/src/lib/common/stdmap.h
new file mode 100644
index 0000000..2351074
--- /dev/null
+++ b/src/lib/common/stdmap.h
@@ -0,0 +1,21 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+#include <map>
+#include "common/stdpost.h"
diff --git a/src/lib/common/stdostream.h b/src/lib/common/stdostream.h
new file mode 100644
index 0000000..bb82285
--- /dev/null
+++ b/src/lib/common/stdostream.h
@@ -0,0 +1,25 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+#if HAVE_OSTREAM
+#include <ostream>
+#else
+#include <iostream>
+#endif
+#include "common/stdpost.h"
diff --git a/src/lib/common/stdpost.h b/src/lib/common/stdpost.h
new file mode 100644
index 0000000..8046da0
--- /dev/null
+++ b/src/lib/common/stdpost.h
@@ -0,0 +1,21 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
diff --git a/src/lib/common/stdpre.h b/src/lib/common/stdpre.h
new file mode 100644
index 0000000..8ccd308
--- /dev/null
+++ b/src/lib/common/stdpre.h
@@ -0,0 +1,31 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#if defined(_MSC_VER)
+#pragma warning(disable: 4786) // identifier truncated
+#pragma warning(disable: 4514) // unreferenced inline
+#pragma warning(disable: 4710) // not inlined
+#pragma warning(disable: 4663) // C++ change, template specialization
+#pragma warning(disable: 4503) // decorated name length too long
+#pragma warning(push, 3)
+#pragma warning(disable: 4018) // signed/unsigned mismatch
+#pragma warning(disable: 4284)
+#pragma warning(disable: 4146) // unary minus on unsigned value
+#pragma warning(disable: 4127) // conditional expression is constant
+#pragma warning(disable: 4701) // variable possibly used uninitialized
+#endif
diff --git a/src/lib/common/stdset.h b/src/lib/common/stdset.h
new file mode 100644
index 0000000..1c98971
--- /dev/null
+++ b/src/lib/common/stdset.h
@@ -0,0 +1,21 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+#include <set>
+#include "common/stdpost.h"
diff --git a/src/lib/common/stdsstream.h b/src/lib/common/stdsstream.h
new file mode 100644
index 0000000..43671ff
--- /dev/null
+++ b/src/lib/common/stdsstream.h
@@ -0,0 +1,376 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+
+#if HAVE_SSTREAM || !defined(__GNUC__) || (__GNUC__ >= 3)
+
+#include <sstream>
+
+#elif defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 95)
+// g++ 2.95 didn't ship with sstream. the following is a backport
+// by Magnus Fromreide of the sstream in g++ 3.0.
+
+/* This is part of libio/iostream, providing -*- C++ -*- input/output.
+Copyright (C) 2012-2016 Symless Ltd.
+Copyright (C) 2000 Free Software Foundation
+
+This file is part of the GNU IO Library. This library is free
+software; you can redistribute it and/or modify it under the
+terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This library 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 library; see the file LICENSE. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+As a special exception, if you link this library with files
+compiled with a GNU compiler to produce an executable, this does not cause
+the resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why
+the executable file might be covered by the GNU General Public License. */
+
+/* Written by Magnus Fromreide (magfr@lysator.liu.se). */
+/* seekoff and ideas for overflow is largely borrowed from libstdc++-v3 */
+
+#include <iostream.h>
+#include <streambuf.h>
+#include <string>
+
+namespace std
+{
+ class stringbuf : public streambuf
+ {
+ public:
+ typedef char char_type;
+ typedef int int_type;
+ typedef streampos pos_type;
+ typedef streamoff off_type;
+
+ explicit
+ stringbuf(int which=ios::in|ios::out)
+ : streambuf(), mode(static_cast<ios::open_mode>(which)),
+ stream(NULL), stream_len(0)
+ {
+ stringbuf_init();
+ }
+
+ explicit
+ stringbuf(const string &str, int which=ios::in|ios::out)
+ : streambuf(), mode(static_cast<ios::open_mode>(which)),
+ stream(NULL), stream_len(0)
+ {
+ if (mode & (ios::in|ios::out))
+ {
+ stream_len = str.size();
+ stream = new char_type[stream_len];
+ str.copy(stream, stream_len);
+ }
+ stringbuf_init();
+ }
+
+ virtual
+ ~stringbuf()
+ {
+ delete[] stream;
+ }
+
+ string
+ str() const
+ {
+ if (pbase() != 0)
+ return string(stream, pptr()-pbase());
+ else
+ return string();
+ }
+
+ void
+ str(const string& str)
+ {
+ delete[] stream;
+ stream_len = str.size();
+ stream = new char_type[stream_len];
+ str.copy(stream, stream_len);
+ stringbuf_init();
+ }
+
+ protected:
+ // The buffer is already in gptr, so if it ends then it is out of data.
+ virtual int
+ underflow()
+ {
+ return EOF;
+ }
+
+ virtual int
+ overflow(int c = EOF)
+ {
+ int res;
+ if (mode & ios::out)
+ {
+ if (c != EOF)
+ {
+ streamsize old_stream_len = stream_len;
+ stream_len += 1;
+ char_type* new_stream = new char_type[stream_len];
+ memcpy(new_stream, stream, old_stream_len);
+ delete[] stream;
+ stream = new_stream;
+ stringbuf_sync(gptr()-eback(), pptr()-pbase());
+ sputc(c);
+ res = c;
+ }
+ else
+ res = EOF;
+ }
+ else
+ res = 0;
+ return res;
+ }
+
+ virtual streambuf*
+ setbuf(char_type* s, streamsize n)
+ {
+ if (n != 0)
+ {
+ delete[] stream;
+ stream = new char_type[n];
+ memcpy(stream, s, n);
+ stream_len = n;
+ stringbuf_sync(0, 0);
+ }
+ return this;
+ }
+
+ virtual pos_type
+ seekoff(off_type off, ios::seek_dir way, int which = ios::in | ios::out)
+ {
+ pos_type ret = pos_type(off_type(-1));
+ bool testin = which & ios::in && mode & ios::in;
+ bool testout = which & ios::out && mode & ios::out;
+ bool testboth = testin && testout && way != ios::cur;
+
+ if (stream_len && ((testin != testout) || testboth))
+ {
+ char_type* beg = stream;
+ char_type* curi = NULL;
+ char_type* curo = NULL;
+ char_type* endi = NULL;
+ char_type* endo = NULL;
+
+ if (testin)
+ {
+ curi = gptr();
+ endi = egptr();
+ }
+ if (testout)
+ {
+ curo = pptr();
+ endo = epptr();
+ }
+
+ off_type newoffi = 0;
+ off_type newoffo = 0;
+ if (way == ios::beg)
+ {
+ newoffi = beg - curi;
+ newoffo = beg - curo;
+ }
+ else if (way == ios::end)
+ {
+ newoffi = endi - curi;
+ newoffo = endo - curo;
+ }
+
+ if (testin && newoffi + off + curi - beg >= 0 &&
+ endi - beg >= newoffi + off + curi - beg)
+ {
+ gbump(newoffi + off);
+ ret = pos_type(newoffi + off + curi);
+ }
+ if (testout && newoffo + off + curo - beg >= 0 &&
+ endo - beg >= newoffo + off + curo - beg)
+ {
+ pbump(newoffo + off);
+ ret = pos_type(newoffo + off + curo);
+ }
+ }
+ return ret;
+ }
+
+ virtual pos_type
+ seekpos(pos_type sp, int which = ios::in | ios::out)
+ {
+ pos_type ret = seekoff(sp, ios::beg, which);
+ return ret;
+ }
+
+ private:
+ void
+ stringbuf_sync(streamsize i, streamsize o)
+ {
+ if (mode & ios::in)
+ setg(stream, stream + i, stream + stream_len);
+ if (mode & ios::out)
+ {
+ setp(stream, stream + stream_len);
+ pbump(o);
+ }
+ }
+ void
+ stringbuf_init()
+ {
+ if (mode & ios::ate)
+ stringbuf_sync(0, stream_len);
+ else
+ stringbuf_sync(0, 0);
+ }
+
+ private:
+ ios::open_mode mode;
+ char_type* stream;
+ streamsize stream_len;
+ };
+
+ class istringstream : public istream {
+ public:
+ typedef char char_type;
+ typedef int int_type;
+ typedef streampos pos_type;
+ typedef streamoff off_type;
+
+ explicit
+ istringstream(int which=ios::in)
+ : istream(&sb), sb(which | ios::in)
+ { }
+
+ explicit
+ istringstream(const string& str, int which=ios::in)
+ : istream(&sb), sb(str, which | ios::in)
+ { }
+
+ stringbuf*
+ rdbuf() const
+ {
+ return const_cast<stringbuf*>(&sb);
+ }
+
+ string
+ str() const
+ {
+ return rdbuf()->str();
+ }
+ void
+ str(const string& s)
+ {
+ rdbuf()->str(s);
+ }
+ private:
+ stringbuf sb;
+ };
+
+ class ostringstream : public ostream {
+ public:
+ typedef char char_type;
+ typedef int int_type;
+ typedef streampos pos_type;
+ typedef streamoff off_type;
+
+ explicit
+ ostringstream(int which=ios::out)
+ : ostream(&sb), sb(which | ios::out)
+ { }
+
+ explicit
+ ostringstream(const string& str, int which=ios::out)
+ : ostream(&sb), sb(str, which | ios::out)
+ { }
+
+ stringbuf*
+ rdbuf() const
+ {
+ return const_cast<stringbuf*>(&sb);
+ }
+
+ string
+ str() const
+ {
+ return rdbuf()->str();
+ }
+
+ void str(const string& s)
+ {
+ rdbuf()->str(s);
+ }
+ private:
+ stringbuf sb;
+ };
+
+ class stringstream : public iostream {
+ public:
+ typedef char char_type;
+ typedef int int_type;
+ typedef streampos pos_type;
+ typedef streamoff off_type;
+
+ explicit
+ stringstream(int which=ios::out|ios::in)
+ : iostream(&sb), sb(which)
+ { }
+
+ explicit
+ stringstream(const string& str, int which=ios::out|ios::in)
+ : iostream(&sb), sb(str, which)
+ { }
+
+ stringbuf*
+ rdbuf() const
+ {
+ return const_cast<stringbuf*>(&sb);
+ }
+
+ string
+ str() const
+ {
+ return rdbuf()->str();
+ }
+
+ void
+ str(const string& s)
+ {
+ rdbuf()->str(s);
+ }
+ private:
+ stringbuf sb;
+ };
+};
+
+#else /* not g++ 2.95 and no <sstream> */
+
+#error "Standard C++ library is missing required sstream header."
+
+#endif /* not g++ 2.95 and no <sstream> */
+
+#include "common/stdpost.h"
+#include "common/stdistream.h"
diff --git a/src/lib/common/stdstring.h b/src/lib/common/stdstring.h
new file mode 100644
index 0000000..f320ca8
--- /dev/null
+++ b/src/lib/common/stdstring.h
@@ -0,0 +1,21 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+#include <string>
+#include "common/stdpost.h"
diff --git a/src/lib/common/stdvector.h b/src/lib/common/stdvector.h
new file mode 100644
index 0000000..ab4b853
--- /dev/null
+++ b/src/lib/common/stdvector.h
@@ -0,0 +1,21 @@
+/*
+ * barrier -- 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 "common/stdpre.h"
+#include <vector>
+#include "common/stdpost.h"
diff --git a/src/lib/io/CMakeLists.txt b/src/lib/io/CMakeLists.txt
new file mode 100644
index 0000000..32ae7ec
--- /dev/null
+++ b/src/lib/io/CMakeLists.txt
@@ -0,0 +1,24 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(io STATIC ${sources})
diff --git a/src/lib/io/IStream.h b/src/lib/io/IStream.h
new file mode 100644
index 0000000..cf93ac4
--- /dev/null
+++ b/src/lib/io/IStream.h
@@ -0,0 +1,120 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "common/IInterface.h"
+#include "base/Event.h"
+#include "base/IEventQueue.h"
+#include "base/EventTypes.h"
+
+class IEventQueue;
+
+namespace barrier {
+
+//! Bidirectional stream interface
+/*!
+Defines the interface for all streams.
+*/
+class IStream : public IInterface {
+public:
+ IStream() { }
+
+ //! @name manipulators
+ //@{
+
+ //! Close the stream
+ /*!
+ Closes the stream. Pending input data and buffered output data
+ are discarded. Use \c flush() before \c close() to send buffered
+ output data. Attempts to \c read() after a close return 0,
+ attempts to \c write() generate output error events, and attempts
+ to \c flush() return immediately.
+ */
+ virtual void close() = 0;
+
+ //! Read from stream
+ /*!
+ Read up to \p n bytes into \p buffer, returning the number read
+ (zero if no data is available or input is shutdown). \p buffer
+ may be NULL in which case the data is discarded.
+ */
+ virtual UInt32 read(void* buffer, UInt32 n) = 0;
+
+ //! Write to stream
+ /*!
+ Write \c n bytes from \c buffer to the stream. If this can't
+ complete immediately it will block. Data may be buffered in
+ order to return more quickly. A output error event is generated
+ when writing fails.
+ */
+ virtual void write(const void* buffer, UInt32 n) = 0;
+
+ //! Flush the stream
+ /*!
+ Waits until all buffered data has been written to the stream.
+ */
+ virtual void flush() = 0;
+
+ //! Shutdown input
+ /*!
+ Shutdown the input side of the stream. Any pending input data is
+ discarded and further reads immediately return 0.
+ */
+ virtual void shutdownInput() = 0;
+
+ //! Shutdown output
+ /*!
+ Shutdown the output side of the stream. Any buffered output data
+ is discarded and further writes generate output error events. Use
+ \c flush() before \c shutdownOutput() to send buffered output data.
+ */
+ virtual void shutdownOutput() = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get event target
+ /*!
+ Returns the event target for events generated by this stream. It
+ should be the source stream in a chain of stream filters.
+ */
+ virtual void* getEventTarget() const = 0;
+
+ //! Test if \c read() will succeed
+ /*!
+ Returns true iff an immediate \c read() will return data. This
+ may or may not be the same as \c getSize() > 0, depending on the
+ stream type.
+ */
+ virtual bool isReady() const = 0;
+
+ //! Get bytes available to read
+ /*!
+ Returns a conservative estimate of the available bytes to read
+ (i.e. a number not greater than the actual number of bytes).
+ Some streams may not be able to determine this and will always
+ return zero.
+ */
+ virtual UInt32 getSize() const = 0;
+
+ //@}
+};
+
+}
diff --git a/src/lib/io/StreamBuffer.cpp b/src/lib/io/StreamBuffer.cpp
new file mode 100644
index 0000000..61f05ba
--- /dev/null
+++ b/src/lib/io/StreamBuffer.cpp
@@ -0,0 +1,146 @@
+/*
+ * barrier -- 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 "io/StreamBuffer.h"
+
+//
+// StreamBuffer
+//
+
+const UInt32 StreamBuffer::kChunkSize = 4096;
+
+StreamBuffer::StreamBuffer() :
+ m_size(0),
+ m_headUsed(0)
+{
+ // do nothing
+}
+
+StreamBuffer::~StreamBuffer()
+{
+ // do nothing
+}
+
+const void*
+StreamBuffer::peek(UInt32 n)
+{
+ assert(n <= m_size);
+
+ // if requesting no data then return NULL so we don't try to access
+ // an empty list.
+ if (n == 0) {
+ return NULL;
+ }
+
+ // reserve space in first chunk
+ ChunkList::iterator head = m_chunks.begin();
+ head->reserve(n + m_headUsed);
+
+ // consolidate chunks into the first chunk until it has n bytes
+ ChunkList::iterator scan = head;
+ ++scan;
+ while (head->size() - m_headUsed < n && scan != m_chunks.end()) {
+ head->insert(head->end(), scan->begin(), scan->end());
+ scan = m_chunks.erase(scan);
+ }
+
+ return static_cast<const void*>(&(head->begin()[m_headUsed]));
+}
+
+void
+StreamBuffer::pop(UInt32 n)
+{
+ // discard all chunks if n is greater than or equal to m_size
+ if (n >= m_size) {
+ m_size = 0;
+ m_headUsed = 0;
+ m_chunks.clear();
+ return;
+ }
+
+ // update size
+ m_size -= n;
+
+ // discard chunks until more than n bytes would've been discarded
+ ChunkList::iterator scan = m_chunks.begin();
+ assert(scan != m_chunks.end());
+ while (scan->size() - m_headUsed <= n) {
+ n -= (UInt32)scan->size() - m_headUsed;
+ m_headUsed = 0;
+ scan = m_chunks.erase(scan);
+ assert(scan != m_chunks.end());
+ }
+
+ // remove left over bytes from the head chunk
+ if (n > 0) {
+ m_headUsed += n;
+ }
+}
+
+void
+StreamBuffer::write(const void* vdata, UInt32 n)
+{
+ assert(vdata != NULL);
+
+ // ignore if no data, otherwise update size
+ if (n == 0) {
+ return;
+ }
+ m_size += n;
+
+ // cast data to bytes
+ const UInt8* data = static_cast<const UInt8*>(vdata);
+
+ // point to last chunk if it has space, otherwise append an empty chunk
+ ChunkList::iterator scan = m_chunks.end();
+ if (scan != m_chunks.begin()) {
+ --scan;
+ if (scan->size() >= kChunkSize) {
+ ++scan;
+ }
+ }
+ if (scan == m_chunks.end()) {
+ scan = m_chunks.insert(scan, Chunk());
+ }
+
+ // append data in chunks
+ while (n > 0) {
+ // choose number of bytes for next chunk
+ assert(scan->size() <= kChunkSize);
+ UInt32 count = kChunkSize - (UInt32)scan->size();
+ if (count > n)
+ count = n;
+
+ // transfer data
+ scan->insert(scan->end(), data, data + count);
+ n -= count;
+ data += count;
+
+ // append another empty chunk if we're not done yet
+ if (n > 0) {
+ ++scan;
+ scan = m_chunks.insert(scan, Chunk());
+ }
+ }
+}
+
+UInt32
+StreamBuffer::getSize() const
+{
+ return m_size;
+}
diff --git a/src/lib/io/StreamBuffer.h b/src/lib/io/StreamBuffer.h
new file mode 100644
index 0000000..49b666b
--- /dev/null
+++ b/src/lib/io/StreamBuffer.h
@@ -0,0 +1,79 @@
+/*
+ * barrier -- 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/EventTypes.h"
+#include "common/stdlist.h"
+#include "common/stdvector.h"
+
+//! FIFO of bytes
+/*!
+This class maintains a FIFO (first-in, last-out) buffer of bytes.
+*/
+class StreamBuffer {
+public:
+ StreamBuffer();
+ ~StreamBuffer();
+
+ //! @name manipulators
+ //@{
+
+ //! Read data without removing from buffer
+ /*!
+ Return a pointer to memory with the next \c n bytes in the buffer
+ (which must be <= getSize()). The caller must not modify the returned
+ memory nor delete it.
+ */
+ const void* peek(UInt32 n);
+
+ //! Discard data
+ /*!
+ Discards the next \c n bytes. If \c n >= getSize() then the buffer
+ is cleared.
+ */
+ void pop(UInt32 n);
+
+ //! Write data to buffer
+ /*!
+ Appends \c n bytes from \c data to the buffer.
+ */
+ void write(const void* data, UInt32 n);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get size of buffer
+ /*!
+ Returns the number of bytes in the buffer.
+ */
+ UInt32 getSize() const;
+
+ //@}
+
+private:
+ static const UInt32 kChunkSize;
+
+ typedef std::vector<UInt8> Chunk;
+ typedef std::list<Chunk> ChunkList;
+
+ ChunkList m_chunks;
+ UInt32 m_size;
+ UInt32 m_headUsed;
+};
diff --git a/src/lib/io/StreamFilter.cpp b/src/lib/io/StreamFilter.cpp
new file mode 100644
index 0000000..170e237
--- /dev/null
+++ b/src/lib/io/StreamFilter.cpp
@@ -0,0 +1,118 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "io/StreamFilter.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+
+//
+// StreamFilter
+//
+
+StreamFilter::StreamFilter(IEventQueue* events, barrier::IStream* stream, bool adoptStream) :
+ m_stream(stream),
+ m_adopted(adoptStream),
+ m_events(events)
+{
+ // replace handlers for m_stream
+ m_events->removeHandlers(m_stream->getEventTarget());
+ m_events->adoptHandler(Event::kUnknown, m_stream->getEventTarget(),
+ new TMethodEventJob<StreamFilter>(this,
+ &StreamFilter::handleUpstreamEvent));
+}
+
+StreamFilter::~StreamFilter()
+{
+ m_events->removeHandler(Event::kUnknown, m_stream->getEventTarget());
+ if (m_adopted) {
+ delete m_stream;
+ }
+}
+
+void
+StreamFilter::close()
+{
+ getStream()->close();
+}
+
+UInt32
+StreamFilter::read(void* buffer, UInt32 n)
+{
+ return getStream()->read(buffer, n);
+}
+
+void
+StreamFilter::write(const void* buffer, UInt32 n)
+{
+ getStream()->write(buffer, n);
+}
+
+void
+StreamFilter::flush()
+{
+ getStream()->flush();
+}
+
+void
+StreamFilter::shutdownInput()
+{
+ getStream()->shutdownInput();
+}
+
+void
+StreamFilter::shutdownOutput()
+{
+ getStream()->shutdownOutput();
+}
+
+void*
+StreamFilter::getEventTarget() const
+{
+ return const_cast<void*>(static_cast<const void*>(this));
+}
+
+bool
+StreamFilter::isReady() const
+{
+ return getStream()->isReady();
+}
+
+UInt32
+StreamFilter::getSize() const
+{
+ return getStream()->getSize();
+}
+
+barrier::IStream*
+StreamFilter::getStream() const
+{
+ return m_stream;
+}
+
+void
+StreamFilter::filterEvent(const Event& event)
+{
+ m_events->dispatchEvent(Event(event.getType(),
+ getEventTarget(), event.getData()));
+}
+
+void
+StreamFilter::handleUpstreamEvent(const Event& event, void*)
+{
+ filterEvent(event);
+}
diff --git a/src/lib/io/StreamFilter.h b/src/lib/io/StreamFilter.h
new file mode 100644
index 0000000..e578e0c
--- /dev/null
+++ b/src/lib/io/StreamFilter.h
@@ -0,0 +1,73 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "io/IStream.h"
+#include "base/IEventQueue.h"
+
+//! A stream filter
+/*!
+This class wraps a stream. Subclasses provide indirect access
+to the wrapped stream, typically performing some filtering.
+*/
+class StreamFilter : public barrier::IStream {
+public:
+ /*!
+ Create a wrapper around \c stream. Iff \c adoptStream is true then
+ this object takes ownership of the stream and will delete it in the
+ d'tor.
+ */
+ StreamFilter(IEventQueue* events, barrier::IStream* stream, bool adoptStream = true);
+ virtual ~StreamFilter();
+
+ // IStream overrides
+ // These all just forward to the underlying stream except getEventTarget.
+ // Override as necessary. getEventTarget returns a pointer to this.
+ virtual void close();
+ virtual UInt32 read(void* buffer, UInt32 n);
+ virtual void write(const void* buffer, UInt32 n);
+ virtual void flush();
+ virtual void shutdownInput();
+ virtual void shutdownOutput();
+ virtual void* getEventTarget() const;
+ virtual bool isReady() const;
+ virtual UInt32 getSize() const;
+
+ //! Get the stream
+ /*!
+ Returns the stream passed to the c'tor.
+ */
+ barrier::IStream* getStream() const;
+
+protected:
+ //! Handle events from source stream
+ /*!
+ Does the event filtering. The default simply dispatches an event
+ identical except using this object as the event target.
+ */
+ virtual void filterEvent(const Event&);
+
+private:
+ void handleUpstreamEvent(const Event&, void*);
+
+private:
+ barrier::IStream* m_stream;
+ bool m_adopted;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/io/XIO.cpp b/src/lib/io/XIO.cpp
new file mode 100644
index 0000000..1299d23
--- /dev/null
+++ b/src/lib/io/XIO.cpp
@@ -0,0 +1,51 @@
+/*
+ * barrier -- 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 "io/XIO.h"
+
+//
+// XIOClosed
+//
+
+String
+XIOClosed::getWhat() const throw()
+{
+ return format("XIOClosed", "already closed");
+}
+
+
+//
+// XIOEndOfStream
+//
+
+String
+XIOEndOfStream::getWhat() const throw()
+{
+ return format("XIOEndOfStream", "reached end of stream");
+}
+
+
+//
+// XIOWouldBlock
+//
+
+String
+XIOWouldBlock::getWhat() const throw()
+{
+ return format("XIOWouldBlock", "stream operation would block");
+}
diff --git a/src/lib/io/XIO.h b/src/lib/io/XIO.h
new file mode 100644
index 0000000..4964441
--- /dev/null
+++ b/src/lib/io/XIO.h
@@ -0,0 +1,49 @@
+/*
+ * barrier -- 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/XBase.h"
+
+//! Generic I/O exception
+XBASE_SUBCLASS(XIO, XBase);
+
+//! I/O closing exception
+/*!
+Thrown if a stream cannot be closed.
+*/
+XBASE_SUBCLASS(XIOClose, XIO);
+
+//! I/O already closed exception
+/*!
+Thrown when attempting to close or perform I/O on an already closed.
+stream.
+*/
+XBASE_SUBCLASS_WHAT(XIOClosed, XIO);
+
+//! I/O end of stream exception
+/*!
+Thrown when attempting to read beyond the end of a stream.
+*/
+XBASE_SUBCLASS_WHAT(XIOEndOfStream, XIO);
+
+//! I/O would block exception
+/*!
+Thrown if an operation on a stream would block.
+*/
+XBASE_SUBCLASS_WHAT(XIOWouldBlock, XIO);
diff --git a/src/lib/ipc/CMakeLists.txt b/src/lib/ipc/CMakeLists.txt
new file mode 100644
index 0000000..3c7302a
--- /dev/null
+++ b/src/lib/ipc/CMakeLists.txt
@@ -0,0 +1,28 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(ipc STATIC ${sources})
+
+if (UNIX)
+ target_link_libraries(ipc arch base common mt io net synlib)
+endif()
diff --git a/src/lib/ipc/Ipc.cpp b/src/lib/ipc/Ipc.cpp
new file mode 100644
index 0000000..78b8407
--- /dev/null
+++ b/src/lib/ipc/Ipc.cpp
@@ -0,0 +1,24 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "ipc/Ipc.h"
+
+const char* kIpcMsgHello = "IHEL%1i";
+const char* kIpcMsgLogLine = "ILOG%s";
+const char* kIpcMsgCommand = "ICMD%s%1i";
+const char* kIpcMsgShutdown = "ISDN";
diff --git a/src/lib/ipc/Ipc.h b/src/lib/ipc/Ipc.h
new file mode 100644
index 0000000..bc69c08
--- /dev/null
+++ b/src/lib/ipc/Ipc.h
@@ -0,0 +1,52 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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
+
+#define IPC_HOST "127.0.0.1"
+#define IPC_PORT 24801
+
+enum EIpcMessage {
+ kIpcHello,
+ kIpcLogLine,
+ kIpcCommand,
+ kIpcShutdown,
+};
+
+enum EIpcClientType {
+ kIpcClientUnknown,
+ kIpcClientGui,
+ kIpcClientNode,
+};
+
+// handshake: node/gui -> daemon
+// $1 = type, the client identifies it's self as gui or node (barrierc/s).
+extern const char* kIpcMsgHello;
+
+// log line: daemon -> gui
+// $1 = aggregate log lines collected from barriers/c or the daemon itself.
+extern const char* kIpcMsgLogLine;
+
+// command: gui -> daemon
+// $1 = command; the command for the daemon to launch, typically the full
+// path to barriers/c. $2 = true when process must be elevated on ms windows.
+extern const char* kIpcMsgCommand;
+
+// shutdown: daemon -> node
+// the daemon tells barriers/c to shut down gracefully.
+extern const char* kIpcMsgShutdown;
diff --git a/src/lib/ipc/IpcClient.cpp b/src/lib/ipc/IpcClient.cpp
new file mode 100644
index 0000000..4eeae5b
--- /dev/null
+++ b/src/lib/ipc/IpcClient.cpp
@@ -0,0 +1,108 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "ipc/IpcClient.h"
+#include "ipc/Ipc.h"
+#include "ipc/IpcServerProxy.h"
+#include "ipc/IpcMessage.h"
+#include "base/TMethodEventJob.h"
+
+//
+// IpcClient
+//
+
+IpcClient::IpcClient(IEventQueue* events, SocketMultiplexer* socketMultiplexer) :
+ m_serverAddress(NetworkAddress(IPC_HOST, IPC_PORT)),
+ m_socket(events, socketMultiplexer, IArchNetwork::kINET),
+ m_server(nullptr),
+ m_events(events)
+{
+ init();
+}
+
+IpcClient::IpcClient(IEventQueue* events, SocketMultiplexer* socketMultiplexer, int port) :
+ m_serverAddress(NetworkAddress(IPC_HOST, port)),
+ m_socket(events, socketMultiplexer, IArchNetwork::kINET),
+ m_server(nullptr),
+ m_events(events)
+{
+ init();
+}
+
+void
+IpcClient::init()
+{
+ m_serverAddress.resolve();
+}
+
+IpcClient::~IpcClient()
+{
+}
+
+void
+IpcClient::connect()
+{
+ m_events->adoptHandler(
+ m_events->forIDataSocket().connected(), m_socket.getEventTarget(),
+ new TMethodEventJob<IpcClient>(
+ this, &IpcClient::handleConnected));
+
+ m_socket.connect(m_serverAddress);
+ m_server = new IpcServerProxy(m_socket, m_events);
+
+ m_events->adoptHandler(
+ m_events->forIpcServerProxy().messageReceived(), m_server,
+ new TMethodEventJob<IpcClient>(
+ this, &IpcClient::handleMessageReceived));
+}
+
+void
+IpcClient::disconnect()
+{
+ m_events->removeHandler(m_events->forIDataSocket().connected(), m_socket.getEventTarget());
+ m_events->removeHandler(m_events->forIpcServerProxy().messageReceived(), m_server);
+
+ m_server->disconnect();
+ delete m_server;
+ m_server = nullptr;
+}
+
+void
+IpcClient::send(const IpcMessage& message)
+{
+ assert(m_server != nullptr);
+ m_server->send(message);
+}
+
+void
+IpcClient::handleConnected(const Event&, void*)
+{
+ m_events->addEvent(Event(
+ m_events->forIpcClient().connected(), this, m_server, Event::kDontFreeData));
+
+ IpcHelloMessage message(kIpcClientNode);
+ send(message);
+}
+
+void
+IpcClient::handleMessageReceived(const Event& e, void*)
+{
+ Event event(m_events->forIpcClient().messageReceived(), this);
+ event.setDataObject(e.getDataObject());
+ m_events->addEvent(event);
+}
diff --git a/src/lib/ipc/IpcClient.h b/src/lib/ipc/IpcClient.h
new file mode 100644
index 0000000..1e9bca6
--- /dev/null
+++ b/src/lib/ipc/IpcClient.h
@@ -0,0 +1,64 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "net/NetworkAddress.h"
+#include "net/TCPSocket.h"
+#include "base/EventTypes.h"
+
+class IpcServerProxy;
+class IpcMessage;
+class IEventQueue;
+class SocketMultiplexer;
+
+//! IPC client for communication between daemon and GUI.
+/*!
+ * See \ref IpcServer description.
+ */
+class IpcClient {
+public:
+ IpcClient(IEventQueue* events, SocketMultiplexer* socketMultiplexer);
+ IpcClient(IEventQueue* events, SocketMultiplexer* socketMultiplexer, int port);
+ virtual ~IpcClient();
+
+ //! @name manipulators
+ //@{
+
+ //! Connects to the IPC server at localhost.
+ void connect();
+
+ //! Disconnects from the IPC server.
+ void disconnect();
+
+ //! Sends a message to the server.
+ void send(const IpcMessage& message);
+
+ //@}
+
+private:
+ void init();
+ void handleConnected(const Event&, void*);
+ void handleMessageReceived(const Event&, void*);
+
+private:
+ NetworkAddress m_serverAddress;
+ TCPSocket m_socket;
+ IpcServerProxy* m_server;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/ipc/IpcClientProxy.cpp b/src/lib/ipc/IpcClientProxy.cpp
new file mode 100644
index 0000000..af85eca
--- /dev/null
+++ b/src/lib/ipc/IpcClientProxy.cpp
@@ -0,0 +1,194 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "ipc/IpcClientProxy.h"
+
+#include "ipc/Ipc.h"
+#include "ipc/IpcMessage.h"
+#include "barrier/ProtocolUtil.h"
+#include "io/IStream.h"
+#include "arch/Arch.h"
+#include "base/TMethodEventJob.h"
+#include "base/Log.h"
+
+//
+// IpcClientProxy
+//
+
+IpcClientProxy::IpcClientProxy(barrier::IStream& stream, IEventQueue* events) :
+ m_stream(stream),
+ m_clientType(kIpcClientUnknown),
+ m_disconnecting(false),
+ m_readMutex(ARCH->newMutex()),
+ m_writeMutex(ARCH->newMutex()),
+ m_events(events)
+{
+ m_events->adoptHandler(
+ m_events->forIStream().inputReady(), stream.getEventTarget(),
+ new TMethodEventJob<IpcClientProxy>(
+ this, &IpcClientProxy::handleData));
+
+ m_events->adoptHandler(
+ m_events->forIStream().outputError(), stream.getEventTarget(),
+ new TMethodEventJob<IpcClientProxy>(
+ this, &IpcClientProxy::handleWriteError));
+
+ m_events->adoptHandler(
+ m_events->forIStream().inputShutdown(), stream.getEventTarget(),
+ new TMethodEventJob<IpcClientProxy>(
+ this, &IpcClientProxy::handleDisconnect));
+
+ m_events->adoptHandler(
+ m_events->forIStream().outputShutdown(), stream.getEventTarget(),
+ new TMethodEventJob<IpcClientProxy>(
+ this, &IpcClientProxy::handleWriteError));
+}
+
+IpcClientProxy::~IpcClientProxy()
+{
+ m_events->removeHandler(
+ m_events->forIStream().inputReady(), m_stream.getEventTarget());
+ m_events->removeHandler(
+ m_events->forIStream().outputError(), m_stream.getEventTarget());
+ m_events->removeHandler(
+ m_events->forIStream().inputShutdown(), m_stream.getEventTarget());
+ m_events->removeHandler(
+ m_events->forIStream().outputShutdown(), m_stream.getEventTarget());
+
+ // don't delete the stream while it's being used.
+ ARCH->lockMutex(m_readMutex);
+ ARCH->lockMutex(m_writeMutex);
+ delete &m_stream;
+ ARCH->unlockMutex(m_readMutex);
+ ARCH->unlockMutex(m_writeMutex);
+
+ ARCH->closeMutex(m_readMutex);
+ ARCH->closeMutex(m_writeMutex);
+}
+
+void
+IpcClientProxy::handleDisconnect(const Event&, void*)
+{
+ disconnect();
+ LOG((CLOG_DEBUG "ipc client disconnected"));
+}
+
+void
+IpcClientProxy::handleWriteError(const Event&, void*)
+{
+ disconnect();
+ LOG((CLOG_DEBUG "ipc client write error"));
+}
+
+void
+IpcClientProxy::handleData(const Event&, void*)
+{
+ // don't allow the dtor to destroy the stream while we're using it.
+ ArchMutexLock lock(m_readMutex);
+
+ LOG((CLOG_DEBUG "start ipc handle data"));
+
+ UInt8 code[4];
+ UInt32 n = m_stream.read(code, 4);
+ while (n != 0) {
+
+ LOG((CLOG_DEBUG "ipc read: %c%c%c%c",
+ code[0], code[1], code[2], code[3]));
+
+ IpcMessage* m = nullptr;
+ if (memcmp(code, kIpcMsgHello, 4) == 0) {
+ m = parseHello();
+ }
+ else if (memcmp(code, kIpcMsgCommand, 4) == 0) {
+ m = parseCommand();
+ }
+ else {
+ LOG((CLOG_ERR "invalid ipc message"));
+ disconnect();
+ }
+
+ // don't delete with this event; the data is passed to a new event.
+ Event e(m_events->forIpcClientProxy().messageReceived(), this, NULL, Event::kDontFreeData);
+ e.setDataObject(m);
+ m_events->addEvent(e);
+
+ n = m_stream.read(code, 4);
+ }
+
+ LOG((CLOG_DEBUG "finished ipc handle data"));
+}
+
+void
+IpcClientProxy::send(const IpcMessage& message)
+{
+ // don't allow other threads to write until we've finished the entire
+ // message. stream write is locked, but only for that single write.
+ // also, don't allow the dtor to destroy the stream while we're using it.
+ ArchMutexLock lock(m_writeMutex);
+
+ LOG((CLOG_DEBUG4 "ipc write: %d", message.type()));
+
+ switch (message.type()) {
+ case kIpcLogLine: {
+ const IpcLogLineMessage& llm = static_cast<const IpcLogLineMessage&>(message);
+ const String logLine = llm.logLine();
+ ProtocolUtil::writef(&m_stream, kIpcMsgLogLine, &logLine);
+ break;
+ }
+
+ case kIpcShutdown:
+ ProtocolUtil::writef(&m_stream, kIpcMsgShutdown);
+ break;
+
+ default:
+ LOG((CLOG_ERR "ipc message not supported: %d", message.type()));
+ break;
+ }
+}
+
+IpcHelloMessage*
+IpcClientProxy::parseHello()
+{
+ UInt8 type;
+ ProtocolUtil::readf(&m_stream, kIpcMsgHello + 4, &type);
+
+ m_clientType = static_cast<EIpcClientType>(type);
+
+ // must be deleted by event handler.
+ return new IpcHelloMessage(m_clientType);
+}
+
+IpcCommandMessage*
+IpcClientProxy::parseCommand()
+{
+ String command;
+ UInt8 elevate;
+ ProtocolUtil::readf(&m_stream, kIpcMsgCommand + 4, &command, &elevate);
+
+ // must be deleted by event handler.
+ return new IpcCommandMessage(command, elevate != 0);
+}
+
+void
+IpcClientProxy::disconnect()
+{
+ LOG((CLOG_DEBUG "ipc disconnect, closing stream"));
+ m_disconnecting = true;
+ m_stream.close();
+ m_events->addEvent(Event(m_events->forIpcClientProxy().disconnected(), this));
+}
diff --git a/src/lib/ipc/IpcClientProxy.h b/src/lib/ipc/IpcClientProxy.h
new file mode 100644
index 0000000..eaa12c7
--- /dev/null
+++ b/src/lib/ipc/IpcClientProxy.h
@@ -0,0 +1,55 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "ipc/Ipc.h"
+#include "arch/IArchMultithread.h"
+#include "base/EventTypes.h"
+#include "base/Event.h"
+
+namespace barrier { class IStream; }
+class IpcMessage;
+class IpcCommandMessage;
+class IpcHelloMessage;
+class IEventQueue;
+
+class IpcClientProxy {
+ friend class IpcServer;
+
+public:
+ IpcClientProxy(barrier::IStream& stream, IEventQueue* events);
+ virtual ~IpcClientProxy();
+
+private:
+ void send(const IpcMessage& message);
+ void handleData(const Event&, void*);
+ void handleDisconnect(const Event&, void*);
+ void handleWriteError(const Event&, void*);
+ IpcHelloMessage* parseHello();
+ IpcCommandMessage* parseCommand();
+ void disconnect();
+
+private:
+ barrier::IStream& m_stream;
+ EIpcClientType m_clientType;
+ bool m_disconnecting;
+ ArchMutex m_readMutex;
+ ArchMutex m_writeMutex;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/ipc/IpcLogOutputter.cpp b/src/lib/ipc/IpcLogOutputter.cpp
new file mode 100644
index 0000000..984793e
--- /dev/null
+++ b/src/lib/ipc/IpcLogOutputter.cpp
@@ -0,0 +1,228 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "ipc/IpcLogOutputter.h"
+
+#include "ipc/IpcServer.h"
+#include "ipc/IpcMessage.h"
+#include "ipc/Ipc.h"
+#include "ipc/IpcClientProxy.h"
+#include "mt/Thread.h"
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+#include "base/Event.h"
+#include "base/EventQueue.h"
+#include "base/TMethodEventJob.h"
+#include "base/TMethodJob.h"
+
+enum EIpcLogOutputter {
+ kBufferMaxSize = 1000,
+ kMaxSendLines = 100,
+ kBufferRateWriteLimit = 1000, // writes per kBufferRateTime
+ kBufferRateTimeLimit = 1 // seconds
+};
+
+IpcLogOutputter::IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType, bool useThread) :
+ m_ipcServer(ipcServer),
+ m_bufferMutex(ARCH->newMutex()),
+ m_sending(false),
+ m_bufferThread(nullptr),
+ m_running(false),
+ m_notifyCond(ARCH->newCondVar()),
+ m_notifyMutex(ARCH->newMutex()),
+ m_bufferThreadId(0),
+ m_bufferWaiting(false),
+ m_bufferMaxSize(kBufferMaxSize),
+ m_bufferRateWriteLimit(kBufferRateWriteLimit),
+ m_bufferRateTimeLimit(kBufferRateTimeLimit),
+ m_bufferWriteCount(0),
+ m_bufferRateStart(ARCH->time()),
+ m_clientType(clientType),
+ m_runningMutex(ARCH->newMutex())
+{
+ if (useThread) {
+ m_bufferThread = new Thread(new TMethodJob<IpcLogOutputter>(
+ this, &IpcLogOutputter::bufferThread));
+ }
+}
+
+IpcLogOutputter::~IpcLogOutputter()
+{
+ close();
+
+ ARCH->closeMutex(m_bufferMutex);
+
+ if (m_bufferThread != nullptr) {
+ m_bufferThread->cancel();
+ m_bufferThread->wait();
+ delete m_bufferThread;
+ }
+
+ ARCH->closeCondVar(m_notifyCond);
+ ARCH->closeMutex(m_notifyMutex);
+}
+
+void
+IpcLogOutputter::open(const char* title)
+{
+}
+
+void
+IpcLogOutputter::close()
+{
+ if (m_bufferThread != nullptr) {
+ ArchMutexLock lock(m_runningMutex);
+ m_running = false;
+ notifyBuffer();
+ m_bufferThread->wait(5);
+ }
+}
+
+void
+IpcLogOutputter::show(bool showIfEmpty)
+{
+}
+
+bool
+IpcLogOutputter::write(ELevel, const char* text)
+{
+ // ignore events from the buffer thread (would cause recursion).
+ if (m_bufferThread != nullptr &&
+ Thread::getCurrentThread().getID() == m_bufferThreadId) {
+ return true;
+ }
+
+ appendBuffer(text);
+ notifyBuffer();
+
+ return true;
+}
+
+void
+IpcLogOutputter::appendBuffer(const String& text)
+{
+ ArchMutexLock lock(m_bufferMutex);
+
+ double elapsed = ARCH->time() - m_bufferRateStart;
+ if (elapsed < m_bufferRateTimeLimit) {
+ if (m_bufferWriteCount >= m_bufferRateWriteLimit) {
+ // discard the log line if we've logged too much.
+ return;
+ }
+ }
+ else {
+ m_bufferWriteCount = 0;
+ m_bufferRateStart = ARCH->time();
+ }
+
+ if (m_buffer.size() >= m_bufferMaxSize) {
+ // if the queue is exceeds size limit,
+ // throw away the oldest item
+ m_buffer.pop_front();
+ }
+
+ m_buffer.push_back(text);
+ m_bufferWriteCount++;
+}
+
+bool
+IpcLogOutputter::isRunning()
+{
+ ArchMutexLock lock(m_runningMutex);
+ return m_running;
+}
+
+void
+IpcLogOutputter::bufferThread(void*)
+{
+ m_bufferThreadId = m_bufferThread->getID();
+ m_running = true;
+
+ try {
+ while (isRunning()) {
+ if (m_buffer.empty() || !m_ipcServer.hasClients(m_clientType)) {
+ ArchMutexLock lock(m_notifyMutex);
+ ARCH->waitCondVar(m_notifyCond, m_notifyMutex, -1);
+ }
+
+ sendBuffer();
+ }
+ }
+ catch (XArch& e) {
+ LOG((CLOG_ERR "ipc log buffer thread error, %s", e.what()));
+ }
+
+ LOG((CLOG_DEBUG "ipc log buffer thread finished"));
+}
+
+void
+IpcLogOutputter::notifyBuffer()
+{
+ ArchMutexLock lock(m_notifyMutex);
+ ARCH->broadcastCondVar(m_notifyCond);
+}
+
+String
+IpcLogOutputter::getChunk(size_t count)
+{
+ ArchMutexLock lock(m_bufferMutex);
+
+ if (m_buffer.size() < count) {
+ count = m_buffer.size();
+ }
+
+ String chunk;
+ for (size_t i = 0; i < count; i++) {
+ chunk.append(m_buffer.front());
+ chunk.append("\n");
+ m_buffer.pop_front();
+ }
+ return chunk;
+}
+
+void
+IpcLogOutputter::sendBuffer()
+{
+ if (m_buffer.empty() || !m_ipcServer.hasClients(m_clientType)) {
+ return;
+ }
+
+ IpcLogLineMessage message(getChunk(kMaxSendLines));
+ m_sending = true;
+ m_ipcServer.send(message, kIpcClientGui);
+ m_sending = false;
+}
+
+void
+IpcLogOutputter::bufferMaxSize(UInt16 bufferMaxSize)
+{
+ m_bufferMaxSize = bufferMaxSize;
+}
+
+UInt16
+IpcLogOutputter::bufferMaxSize() const
+{
+ return m_bufferMaxSize;
+}
+
+void
+IpcLogOutputter::bufferRateLimit(UInt16 writeLimit, double timeLimit)
+{
+ m_bufferRateWriteLimit = writeLimit;
+ m_bufferRateTimeLimit = timeLimit;
+}
diff --git a/src/lib/ipc/IpcLogOutputter.h b/src/lib/ipc/IpcLogOutputter.h
new file mode 100644
index 0000000..461f022
--- /dev/null
+++ b/src/lib/ipc/IpcLogOutputter.h
@@ -0,0 +1,119 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "arch/Arch.h"
+#include "arch/IArchMultithread.h"
+#include "base/ILogOutputter.h"
+#include "ipc/Ipc.h"
+
+#include <deque>
+
+class IpcServer;
+class Event;
+class IpcClientProxy;
+
+//! Write log to GUI over IPC
+/*!
+This outputter writes output to the GUI via IPC.
+*/
+class IpcLogOutputter : public ILogOutputter {
+public:
+ /*!
+ If \p useThread is \c true, the buffer will be sent using a thread.
+ If \p useThread is \c false, then the buffer needs to be sent manually
+ using the \c sendBuffer() function.
+ */
+ IpcLogOutputter(IpcServer& ipcServer, EIpcClientType clientType, bool useThread);
+ virtual ~IpcLogOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+
+ //! @name manipulators
+ //@{
+
+ //! Notify that the buffer should be sent.
+ void notifyBuffer();
+
+ //! Set the buffer size
+ /*!
+ Set the maximum size of the buffer to protect memory
+ from runaway logging.
+ */
+ void bufferMaxSize(UInt16 bufferMaxSize);
+
+ //! Set the rate limit
+ /*!
+ Set the maximum number of \p writeRate for every \p timeRate in seconds.
+ */
+ void bufferRateLimit(UInt16 writeLimit, double timeLimit);
+
+ //! Send the buffer
+ /*!
+ Sends a chunk of the buffer to the IPC server, normally called
+ when threaded mode is on.
+ */
+ void sendBuffer();
+
+ //@}
+
+ //! @name accessors
+ //@{
+
+ //! Get the buffer size
+ /*!
+ Returns the maximum size of the buffer.
+ */
+ UInt16 bufferMaxSize() const;
+
+ //@}
+
+private:
+ void init();
+ void bufferThread(void*);
+ String getChunk(size_t count);
+ void appendBuffer(const String& text);
+ bool isRunning();
+
+private:
+ typedef std::deque<String> Buffer;
+
+ IpcServer& m_ipcServer;
+ Buffer m_buffer;
+ ArchMutex m_bufferMutex;
+ bool m_sending;
+ Thread* m_bufferThread;
+ bool m_running;
+ ArchCond m_notifyCond;
+ ArchMutex m_notifyMutex;
+ bool m_bufferWaiting;
+ IArchMultithread::ThreadID
+ m_bufferThreadId;
+ UInt16 m_bufferMaxSize;
+ UInt16 m_bufferRateWriteLimit;
+ double m_bufferRateTimeLimit;
+ UInt16 m_bufferWriteCount;
+ double m_bufferRateStart;
+ EIpcClientType m_clientType;
+ ArchMutex m_runningMutex;
+};
diff --git a/src/lib/ipc/IpcMessage.cpp b/src/lib/ipc/IpcMessage.cpp
new file mode 100644
index 0000000..deef22d
--- /dev/null
+++ b/src/lib/ipc/IpcMessage.cpp
@@ -0,0 +1,69 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "ipc/IpcMessage.h"
+#include "ipc/Ipc.h"
+
+IpcMessage::IpcMessage(UInt8 type) :
+ m_type(type)
+{
+}
+
+IpcMessage::~IpcMessage()
+{
+}
+
+IpcHelloMessage::IpcHelloMessage(EIpcClientType clientType) :
+ IpcMessage(kIpcHello),
+ m_clientType(clientType)
+{
+}
+
+IpcHelloMessage::~IpcHelloMessage()
+{
+}
+
+IpcShutdownMessage::IpcShutdownMessage() :
+IpcMessage(kIpcShutdown)
+{
+}
+
+IpcShutdownMessage::~IpcShutdownMessage()
+{
+}
+
+IpcLogLineMessage::IpcLogLineMessage(const String& logLine) :
+IpcMessage(kIpcLogLine),
+m_logLine(logLine)
+{
+}
+
+IpcLogLineMessage::~IpcLogLineMessage()
+{
+}
+
+IpcCommandMessage::IpcCommandMessage(const String& command, bool elevate) :
+IpcMessage(kIpcCommand),
+m_command(command),
+m_elevate(elevate)
+{
+}
+
+IpcCommandMessage::~IpcCommandMessage()
+{
+}
diff --git a/src/lib/ipc/IpcMessage.h b/src/lib/ipc/IpcMessage.h
new file mode 100644
index 0000000..5cc3d79
--- /dev/null
+++ b/src/lib/ipc/IpcMessage.h
@@ -0,0 +1,85 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "ipc/Ipc.h"
+#include "base/EventTypes.h"
+#include "base/String.h"
+#include "base/Event.h"
+
+class IpcMessage : public EventData {
+public:
+ virtual ~IpcMessage();
+
+ //! Gets the message type ID.
+ UInt8 type() const { return m_type; }
+
+protected:
+ IpcMessage(UInt8 type);
+
+private:
+ UInt8 m_type;
+};
+
+class IpcHelloMessage : public IpcMessage {
+public:
+ IpcHelloMessage(EIpcClientType clientType);
+ virtual ~IpcHelloMessage();
+
+ //! Gets the message type ID.
+ EIpcClientType clientType() const { return m_clientType; }
+
+private:
+ EIpcClientType m_clientType;
+};
+
+class IpcShutdownMessage : public IpcMessage {
+public:
+ IpcShutdownMessage();
+ virtual ~IpcShutdownMessage();
+};
+
+
+class IpcLogLineMessage : public IpcMessage {
+public:
+ IpcLogLineMessage(const String& logLine);
+ virtual ~IpcLogLineMessage();
+
+ //! Gets the log line.
+ String logLine() const { return m_logLine; }
+
+private:
+ String m_logLine;
+};
+
+class IpcCommandMessage : public IpcMessage {
+public:
+ IpcCommandMessage(const String& command, bool elevate);
+ virtual ~IpcCommandMessage();
+
+ //! Gets the command.
+ String command() const { return m_command; }
+
+ //! Gets whether or not the process should be elevated on MS Windows.
+ bool elevate() const { return m_elevate; }
+
+private:
+ String m_command;
+ bool m_elevate;
+};
diff --git a/src/lib/ipc/IpcServer.cpp b/src/lib/ipc/IpcServer.cpp
new file mode 100644
index 0000000..e05a913
--- /dev/null
+++ b/src/lib/ipc/IpcServer.cpp
@@ -0,0 +1,187 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "ipc/IpcServer.h"
+
+#include "ipc/Ipc.h"
+#include "ipc/IpcClientProxy.h"
+#include "ipc/IpcMessage.h"
+#include "net/IDataSocket.h"
+#include "io/IStream.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+#include "base/Event.h"
+#include "base/Log.h"
+
+//
+// IpcServer
+//
+
+IpcServer::IpcServer(IEventQueue* events, SocketMultiplexer* socketMultiplexer) :
+ m_mock(false),
+ m_events(events),
+ m_socketMultiplexer(socketMultiplexer),
+ m_socket(nullptr),
+ m_address(NetworkAddress(IPC_HOST, IPC_PORT))
+{
+ init();
+}
+
+IpcServer::IpcServer(IEventQueue* events, SocketMultiplexer* socketMultiplexer, int port) :
+ m_mock(false),
+ m_events(events),
+ m_socketMultiplexer(socketMultiplexer),
+ m_address(NetworkAddress(IPC_HOST, port))
+{
+ init();
+}
+
+void
+IpcServer::init()
+{
+ m_socket = new TCPListenSocket(m_events, m_socketMultiplexer, IArchNetwork::kINET);
+
+ m_clientsMutex = ARCH->newMutex();
+ m_address.resolve();
+
+ m_events->adoptHandler(
+ m_events->forIListenSocket().connecting(), m_socket,
+ new TMethodEventJob<IpcServer>(
+ this, &IpcServer::handleClientConnecting));
+}
+
+IpcServer::~IpcServer()
+{
+ if (m_mock) {
+ return;
+ }
+
+ if (m_socket != nullptr) {
+ delete m_socket;
+ }
+
+ ARCH->lockMutex(m_clientsMutex);
+ ClientList::iterator it;
+ for (it = m_clients.begin(); it != m_clients.end(); it++) {
+ deleteClient(*it);
+ }
+ m_clients.clear();
+ ARCH->unlockMutex(m_clientsMutex);
+ ARCH->closeMutex(m_clientsMutex);
+
+ m_events->removeHandler(m_events->forIListenSocket().connecting(), m_socket);
+}
+
+void
+IpcServer::listen()
+{
+ m_socket->bind(m_address);
+}
+
+void
+IpcServer::handleClientConnecting(const Event&, void*)
+{
+ barrier::IStream* stream = m_socket->accept();
+ if (stream == NULL) {
+ return;
+ }
+
+ LOG((CLOG_DEBUG "accepted ipc client connection"));
+
+ ARCH->lockMutex(m_clientsMutex);
+ IpcClientProxy* proxy = new IpcClientProxy(*stream, m_events);
+ m_clients.push_back(proxy);
+ ARCH->unlockMutex(m_clientsMutex);
+
+ m_events->adoptHandler(
+ m_events->forIpcClientProxy().disconnected(), proxy,
+ new TMethodEventJob<IpcServer>(
+ this, &IpcServer::handleClientDisconnected));
+
+ m_events->adoptHandler(
+ m_events->forIpcClientProxy().messageReceived(), proxy,
+ new TMethodEventJob<IpcServer>(
+ this, &IpcServer::handleMessageReceived));
+
+ m_events->addEvent(Event(
+ m_events->forIpcServer().clientConnected(), this, proxy, Event::kDontFreeData));
+}
+
+void
+IpcServer::handleClientDisconnected(const Event& e, void*)
+{
+ IpcClientProxy* proxy = static_cast<IpcClientProxy*>(e.getTarget());
+
+ ArchMutexLock lock(m_clientsMutex);
+ m_clients.remove(proxy);
+ deleteClient(proxy);
+
+ LOG((CLOG_DEBUG "ipc client proxy removed, connected=%d", m_clients.size()));
+}
+
+void
+IpcServer::handleMessageReceived(const Event& e, void*)
+{
+ Event event(m_events->forIpcServer().messageReceived(), this);
+ event.setDataObject(e.getDataObject());
+ m_events->addEvent(event);
+}
+
+void
+IpcServer::deleteClient(IpcClientProxy* proxy)
+{
+ m_events->removeHandler(m_events->forIpcClientProxy().messageReceived(), proxy);
+ m_events->removeHandler(m_events->forIpcClientProxy().disconnected(), proxy);
+ delete proxy;
+}
+
+bool
+IpcServer::hasClients(EIpcClientType clientType) const
+{
+ ArchMutexLock lock(m_clientsMutex);
+
+ if (m_clients.empty()) {
+ return false;
+ }
+
+ ClientList::const_iterator it;
+ for (it = m_clients.begin(); it != m_clients.end(); it++) {
+ // at least one client is alive and type matches, there are clients.
+ IpcClientProxy* p = *it;
+ if (!p->m_disconnecting && p->m_clientType == clientType) {
+ return true;
+ }
+ }
+
+ // all clients must be disconnecting, no active clients.
+ return false;
+}
+
+void
+IpcServer::send(const IpcMessage& message, EIpcClientType filterType)
+{
+ ArchMutexLock lock(m_clientsMutex);
+
+ ClientList::iterator it;
+ for (it = m_clients.begin(); it != m_clients.end(); it++) {
+ IpcClientProxy* proxy = *it;
+ if (proxy->m_clientType == filterType) {
+ proxy->send(message);
+ }
+ }
+}
diff --git a/src/lib/ipc/IpcServer.h b/src/lib/ipc/IpcServer.h
new file mode 100644
index 0000000..d9bbe3e
--- /dev/null
+++ b/src/lib/ipc/IpcServer.h
@@ -0,0 +1,92 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "ipc/Ipc.h"
+#include "net/TCPListenSocket.h"
+#include "net/NetworkAddress.h"
+#include "arch/Arch.h"
+#include "base/EventTypes.h"
+
+#include <list>
+
+class Event;
+class IpcClientProxy;
+class IpcMessage;
+class IEventQueue;
+class SocketMultiplexer;
+
+//! IPC server for communication between daemon and GUI.
+/*!
+The IPC server listens on localhost. The IPC client runs on both the
+client/server process or the GUI. The IPC server runs on the daemon process.
+This allows the GUI to send config changes to the daemon and client/server,
+and allows the daemon and client/server to send log data to the GUI.
+*/
+class IpcServer {
+public:
+ IpcServer(IEventQueue* events, SocketMultiplexer* socketMultiplexer);
+ IpcServer(IEventQueue* events, SocketMultiplexer* socketMultiplexer, int port);
+ virtual ~IpcServer();
+
+ //! @name manipulators
+ //@{
+
+ //! Opens a TCP socket only allowing local connections.
+ virtual void listen();
+
+ //! Send a message to all clients matching the filter type.
+ virtual void send(const IpcMessage& message, EIpcClientType filterType);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Returns true when there are clients of the specified type connected.
+ virtual bool hasClients(EIpcClientType clientType) const;
+
+ //@}
+
+private:
+ void init();
+ void handleClientConnecting(const Event&, void*);
+ void handleClientDisconnected(const Event&, void*);
+ void handleMessageReceived(const Event&, void*);
+ void deleteClient(IpcClientProxy* proxy);
+
+private:
+ typedef std::list<IpcClientProxy*> ClientList;
+
+ bool m_mock;
+ IEventQueue* m_events;
+ SocketMultiplexer* m_socketMultiplexer;
+ TCPListenSocket* m_socket;
+ NetworkAddress m_address;
+ ClientList m_clients;
+ ArchMutex m_clientsMutex;
+
+#ifdef TEST_ENV
+public:
+ IpcServer() :
+ m_mock(true),
+ m_events(nullptr),
+ m_socketMultiplexer(nullptr),
+ m_socket(nullptr) { }
+#endif
+};
diff --git a/src/lib/ipc/IpcServerProxy.cpp b/src/lib/ipc/IpcServerProxy.cpp
new file mode 100644
index 0000000..820e1ab
--- /dev/null
+++ b/src/lib/ipc/IpcServerProxy.cpp
@@ -0,0 +1,123 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "ipc/IpcServerProxy.h"
+
+#include "ipc/IpcMessage.h"
+#include "ipc/Ipc.h"
+#include "barrier/ProtocolUtil.h"
+#include "io/IStream.h"
+#include "base/TMethodEventJob.h"
+#include "base/Log.h"
+
+//
+// IpcServerProxy
+//
+
+IpcServerProxy::IpcServerProxy(barrier::IStream& stream, IEventQueue* events) :
+ m_stream(stream),
+ m_events(events)
+{
+ m_events->adoptHandler(m_events->forIStream().inputReady(),
+ stream.getEventTarget(),
+ new TMethodEventJob<IpcServerProxy>(
+ this, &IpcServerProxy::handleData));
+}
+
+IpcServerProxy::~IpcServerProxy()
+{
+ m_events->removeHandler(m_events->forIStream().inputReady(),
+ m_stream.getEventTarget());
+}
+
+void
+IpcServerProxy::handleData(const Event&, void*)
+{
+ LOG((CLOG_DEBUG "start ipc handle data"));
+
+ UInt8 code[4];
+ UInt32 n = m_stream.read(code, 4);
+ while (n != 0) {
+
+ LOG((CLOG_DEBUG "ipc read: %c%c%c%c",
+ code[0], code[1], code[2], code[3]));
+
+ IpcMessage* m = nullptr;
+ if (memcmp(code, kIpcMsgLogLine, 4) == 0) {
+ m = parseLogLine();
+ }
+ else if (memcmp(code, kIpcMsgShutdown, 4) == 0) {
+ m = new IpcShutdownMessage();
+ }
+ else {
+ LOG((CLOG_ERR "invalid ipc message"));
+ disconnect();
+ }
+
+ // don't delete with this event; the data is passed to a new event.
+ Event e(m_events->forIpcServerProxy().messageReceived(), this, NULL, Event::kDontFreeData);
+ e.setDataObject(m);
+ m_events->addEvent(e);
+
+ n = m_stream.read(code, 4);
+ }
+
+ LOG((CLOG_DEBUG "finished ipc handle data"));
+}
+
+void
+IpcServerProxy::send(const IpcMessage& message)
+{
+ LOG((CLOG_DEBUG4 "ipc write: %d", message.type()));
+
+ switch (message.type()) {
+ case kIpcHello: {
+ const IpcHelloMessage& hm = static_cast<const IpcHelloMessage&>(message);
+ ProtocolUtil::writef(&m_stream, kIpcMsgHello, hm.clientType());
+ break;
+ }
+
+ case kIpcCommand: {
+ const IpcCommandMessage& cm = static_cast<const IpcCommandMessage&>(message);
+ const String command = cm.command();
+ ProtocolUtil::writef(&m_stream, kIpcMsgCommand, &command);
+ break;
+ }
+
+ default:
+ LOG((CLOG_ERR "ipc message not supported: %d", message.type()));
+ break;
+ }
+}
+
+IpcLogLineMessage*
+IpcServerProxy::parseLogLine()
+{
+ String logLine;
+ ProtocolUtil::readf(&m_stream, kIpcMsgLogLine + 4, &logLine);
+
+ // must be deleted by event handler.
+ return new IpcLogLineMessage(logLine);
+}
+
+void
+IpcServerProxy::disconnect()
+{
+ LOG((CLOG_DEBUG "ipc disconnect, closing stream"));
+ m_stream.close();
+}
diff --git a/src/lib/ipc/IpcServerProxy.h b/src/lib/ipc/IpcServerProxy.h
new file mode 100644
index 0000000..f2218a4
--- /dev/null
+++ b/src/lib/ipc/IpcServerProxy.h
@@ -0,0 +1,46 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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/Event.h"
+#include "base/EventTypes.h"
+
+namespace barrier { class IStream; }
+class IpcMessage;
+class IpcLogLineMessage;
+class IEventQueue;
+
+class IpcServerProxy {
+ friend class IpcClient;
+
+public:
+ IpcServerProxy(barrier::IStream& stream, IEventQueue* events);
+ virtual ~IpcServerProxy();
+
+private:
+ void send(const IpcMessage& message);
+
+ void handleData(const Event&, void*);
+ IpcLogLineMessage* parseLogLine();
+ void disconnect();
+
+private:
+ barrier::IStream& m_stream;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/mt/CMakeLists.txt b/src/lib/mt/CMakeLists.txt
new file mode 100644
index 0000000..9ee5ea4
--- /dev/null
+++ b/src/lib/mt/CMakeLists.txt
@@ -0,0 +1,24 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(mt STATIC ${sources})
diff --git a/src/lib/mt/CondVar.cpp b/src/lib/mt/CondVar.cpp
new file mode 100644
index 0000000..11318f9
--- /dev/null
+++ b/src/lib/mt/CondVar.cpp
@@ -0,0 +1,91 @@
+/*
+ * barrier -- 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 "mt/CondVar.h"
+#include "arch/Arch.h"
+#include "base/Stopwatch.h"
+
+//
+// CondVarBase
+//
+
+CondVarBase::CondVarBase(Mutex* mutex) :
+ m_mutex(mutex)
+{
+ assert(m_mutex != NULL);
+ m_cond = ARCH->newCondVar();
+}
+
+CondVarBase::~CondVarBase()
+{
+ ARCH->closeCondVar(m_cond);
+}
+
+void
+CondVarBase::lock() const
+{
+ m_mutex->lock();
+}
+
+void
+CondVarBase::unlock() const
+{
+ m_mutex->unlock();
+}
+
+void
+CondVarBase::signal()
+{
+ ARCH->signalCondVar(m_cond);
+}
+
+void
+CondVarBase::broadcast()
+{
+ ARCH->broadcastCondVar(m_cond);
+}
+
+bool
+CondVarBase::wait(Stopwatch& timer, double timeout) const
+{
+ double remain = timeout-timer.getTime();
+ // Some ARCH wait()s return prematurely, retry until really timed out
+ // In particular, ArchMultithreadPosix::waitCondVar() returns every 100ms
+ do {
+ // Always call wait at least once, even if remain is 0, to give
+ // other thread a chance to grab the mutex to avoid deadlocks on
+ // busy waiting.
+ if (remain<0.0) remain=0.0;
+ if (wait(remain))
+ return true;
+ remain = timeout - timer.getTime();
+ } while (remain >= 0.0);
+ return false;
+}
+
+bool
+CondVarBase::wait(double timeout) const
+{
+ return ARCH->waitCondVar(m_cond, m_mutex->m_mutex, timeout);
+}
+
+Mutex*
+CondVarBase::getMutex() const
+{
+ return m_mutex;
+}
diff --git a/src/lib/mt/CondVar.h b/src/lib/mt/CondVar.h
new file mode 100644
index 0000000..0ab956b
--- /dev/null
+++ b/src/lib/mt/CondVar.h
@@ -0,0 +1,225 @@
+/*
+ * barrier -- 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 "mt/Mutex.h"
+#include "common/basic_types.h"
+
+class Stopwatch;
+
+//! Generic condition variable
+/*!
+This class provides functionality common to all condition variables
+but doesn't provide the actual variable storage. A condition variable
+is a multiprocessing primitive that can be waited on. Every condition
+variable has an associated mutex.
+*/
+class CondVarBase {
+public:
+ /*!
+ \c mutex must not be NULL. All condition variables have an
+ associated mutex. The mutex needn't be unique to one condition
+ variable.
+ */
+ CondVarBase(Mutex* mutex);
+ ~CondVarBase();
+
+ //! @name manipulators
+ //@{
+
+ //! Lock the condition variable's mutex
+ /*!
+ Lock the condition variable's mutex. The condition variable should
+ be locked before reading or writing it. It must be locked for a
+ call to wait(). Locks are not recursive; locking a locked mutex
+ will deadlock the thread.
+ */
+ void lock() const;
+
+ //! Unlock the condition variable's mutex
+ void unlock() const;
+
+ //! Signal the condition variable
+ /*!
+ Wake up one waiting thread, if there are any. Which thread gets
+ woken is undefined.
+ */
+ void signal();
+
+ //! Signal the condition variable
+ /*!
+ Wake up all waiting threads, if any.
+ */
+ void broadcast();
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Wait on the condition variable
+ /*!
+ Wait on the condition variable. If \c timeout < 0 then wait until
+ signalled, otherwise up to \c timeout seconds or until signalled,
+ whichever comes first. Returns true if the object was signalled
+ during the wait, false otherwise.
+
+ The proper way to wait for a condition is:
+ \code
+ cv.lock();
+ while (cv-expr) {
+ cv.wait();
+ }
+ cv.unlock();
+ \endcode
+ where \c cv-expr involves the value of \c cv and is false when the
+ condition is satisfied.
+
+ (cancellation point)
+ */
+ bool wait(double timeout = -1.0) const;
+
+ //! Wait on the condition variable
+ /*!
+ Same as \c wait(double) but use \c timer to compare against \c timeout.
+ Since clients normally wait on condition variables in a loop, clients
+ can use this to avoid recalculating \c timeout on each iteration.
+ Passing a stopwatch with a negative \c timeout is pointless (it will
+ never time out) but permitted.
+
+ (cancellation point)
+ */
+ bool wait(Stopwatch& timer, double timeout) const;
+
+ //! Get the mutex
+ /*!
+ Get the mutex passed to the c'tor.
+ */
+ Mutex* getMutex() const;
+
+ //@}
+
+private:
+ // not implemented
+ CondVarBase(const CondVarBase&);
+ CondVarBase& operator=(const CondVarBase&);
+
+private:
+ Mutex* m_mutex;
+ ArchCond m_cond;
+};
+
+//! Condition variable
+/*!
+A condition variable with storage for type \c T.
+*/
+template <class T>
+class CondVar : public CondVarBase {
+public:
+ //! Initialize using \c value
+ CondVar(Mutex* mutex, const T& value);
+ //! Initialize using another condition variable's value
+ CondVar(const CondVar&);
+ ~CondVar();
+
+ //! @name manipulators
+ //@{
+
+ //! Assigns the value of \c cv to this
+ /*!
+ Set the variable's value. The condition variable should be locked
+ before calling this method.
+ */
+ CondVar& operator=(const CondVar& cv);
+
+ //! Assigns \c value to this
+ /*!
+ Set the variable's value. The condition variable should be locked
+ before calling this method.
+ */
+ CondVar& operator=(const T& v);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get the variable's value
+ /*!
+ Get the variable's value. The condition variable should be locked
+ before calling this method.
+ */
+ operator const volatile T&() const;
+
+ //@}
+
+private:
+ volatile T m_data;
+};
+
+template <class T>
+inline
+CondVar<T>::CondVar(
+ Mutex* mutex,
+ const T& data) :
+ CondVarBase(mutex),
+ m_data(data)
+{
+ // do nothing
+}
+
+template <class T>
+inline
+CondVar<T>::CondVar(
+ const CondVar& cv) :
+ CondVarBase(cv.getMutex()),
+ m_data(cv.m_data)
+{
+ // do nothing
+}
+
+template <class T>
+inline
+CondVar<T>::~CondVar()
+{
+ // do nothing
+}
+
+template <class T>
+inline
+CondVar<T>&
+CondVar<T>::operator=(const CondVar<T>& cv)
+{
+ m_data = cv.m_data;
+ return *this;
+}
+
+template <class T>
+inline
+CondVar<T>&
+CondVar<T>::operator=(const T& data)
+{
+ m_data = data;
+ return *this;
+}
+
+template <class T>
+inline
+CondVar<T>::operator const volatile T&() const
+{
+ return m_data;
+}
diff --git a/src/lib/mt/Lock.cpp b/src/lib/mt/Lock.cpp
new file mode 100644
index 0000000..80721b9
--- /dev/null
+++ b/src/lib/mt/Lock.cpp
@@ -0,0 +1,42 @@
+/*
+ * barrier -- 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 "mt/Lock.h"
+#include "mt/CondVar.h"
+#include "mt/Mutex.h"
+
+//
+// Lock
+//
+
+Lock::Lock(const Mutex* mutex) :
+ m_mutex(mutex)
+{
+ m_mutex->lock();
+}
+
+Lock::Lock(const CondVarBase* cv) :
+ m_mutex(cv->getMutex())
+{
+ m_mutex->lock();
+}
+
+Lock::~Lock()
+{
+ m_mutex->unlock();
+}
diff --git a/src/lib/mt/Lock.h b/src/lib/mt/Lock.h
new file mode 100644
index 0000000..4a3f311
--- /dev/null
+++ b/src/lib/mt/Lock.h
@@ -0,0 +1,49 @@
+/*
+ * barrier -- 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 "common/common.h"
+
+class Mutex;
+class CondVarBase;
+
+//! Mutual exclusion lock utility
+/*!
+This class locks a mutex or condition variable in the c'tor and unlocks
+it in the d'tor. It's easier and safer than manually locking and
+unlocking since unlocking must usually be done no matter how a function
+exits (including by unwinding due to an exception).
+*/
+class Lock {
+public:
+ //! Lock the mutex \c mutex
+ Lock(const Mutex* mutex);
+ //! Lock the condition variable \c cv
+ Lock(const CondVarBase* cv);
+ //! Unlock the mutex or condition variable
+ ~Lock();
+
+private:
+ // not implemented
+ Lock(const Lock&);
+ Lock& operator=(const Lock&);
+
+private:
+ const Mutex* m_mutex;
+};
diff --git a/src/lib/mt/Mutex.cpp b/src/lib/mt/Mutex.cpp
new file mode 100644
index 0000000..e9a62e8
--- /dev/null
+++ b/src/lib/mt/Mutex.cpp
@@ -0,0 +1,58 @@
+/*
+ * barrier -- 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 "mt/Mutex.h"
+
+#include "arch/Arch.h"
+
+//
+// Mutex
+//
+
+Mutex::Mutex()
+{
+ m_mutex = ARCH->newMutex();
+}
+
+Mutex::Mutex(const Mutex&)
+{
+ m_mutex = ARCH->newMutex();
+}
+
+Mutex::~Mutex()
+{
+ ARCH->closeMutex(m_mutex);
+}
+
+Mutex&
+Mutex::operator=(const Mutex&)
+{
+ return *this;
+}
+
+void
+Mutex::lock() const
+{
+ ARCH->lockMutex(m_mutex);
+}
+
+void
+Mutex::unlock() const
+{
+ ARCH->unlockMutex(m_mutex);
+}
diff --git a/src/lib/mt/Mutex.h b/src/lib/mt/Mutex.h
new file mode 100644
index 0000000..51a9649
--- /dev/null
+++ b/src/lib/mt/Mutex.h
@@ -0,0 +1,79 @@
+/*
+ * barrier -- 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 "arch/IArchMultithread.h"
+
+//! Mutual exclusion
+/*!
+A non-recursive mutual exclusion object. Only one thread at a time can
+hold a lock on a mutex. Any thread that attempts to lock a locked mutex
+will block until the mutex is unlocked. At that time, if any threads are
+blocked, exactly one waiting thread will acquire the lock and continue
+running. A thread may not lock a mutex it already owns the lock on; if
+it tries it will deadlock itself.
+*/
+class Mutex {
+public:
+ Mutex();
+ //! Equivalent to default c'tor
+ /*!
+ Copy c'tor doesn't copy anything. It just makes it possible to
+ copy objects that contain a mutex.
+ */
+ Mutex(const Mutex&);
+ ~Mutex();
+
+ //! @name manipulators
+ //@{
+
+ //! Does nothing
+ /*!
+ This does nothing. It just makes it possible to assign objects
+ that contain a mutex.
+ */
+ Mutex& operator=(const Mutex&);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Lock the mutex
+ /*!
+ Locks the mutex, which must not have been previously locked by the
+ calling thread. This blocks if the mutex is already locked by another
+ thread.
+
+ (cancellation point)
+ */
+ void lock() const;
+
+ //! Unlock the mutex
+ /*!
+ Unlocks the mutex, which must have been previously locked by the
+ calling thread.
+ */
+ void unlock() const;
+
+ //@}
+
+private:
+ friend class CondVarBase;
+ ArchMutex m_mutex;
+};
diff --git a/src/lib/mt/Thread.cpp b/src/lib/mt/Thread.cpp
new file mode 100644
index 0000000..7474c16
--- /dev/null
+++ b/src/lib/mt/Thread.cpp
@@ -0,0 +1,187 @@
+/*
+ * barrier -- 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 "mt/Thread.h"
+
+#include "mt/XMT.h"
+#include "mt/XThread.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+#include "base/IJob.h"
+
+//
+// Thread
+//
+
+Thread::Thread(IJob* job)
+{
+ m_thread = ARCH->newThread(&Thread::threadFunc, job);
+ if (m_thread == NULL) {
+ // couldn't create thread
+ delete job;
+ throw XMTThreadUnavailable();
+ }
+}
+
+Thread::Thread(const Thread& thread)
+{
+ m_thread = ARCH->copyThread(thread.m_thread);
+}
+
+Thread::Thread(ArchThread adoptedThread)
+{
+ m_thread = adoptedThread;
+}
+
+Thread::~Thread()
+{
+ ARCH->closeThread(m_thread);
+}
+
+Thread&
+Thread::operator=(const Thread& thread)
+{
+ // copy given thread and release ours
+ ArchThread copy = ARCH->copyThread(thread.m_thread);
+ ARCH->closeThread(m_thread);
+
+ // cut over
+ m_thread = copy;
+
+ return *this;
+}
+
+void
+Thread::exit(void* result)
+{
+ throw XThreadExit(result);
+}
+
+void
+Thread::cancel()
+{
+ ARCH->cancelThread(m_thread);
+}
+
+void
+Thread::setPriority(int n)
+{
+ ARCH->setPriorityOfThread(m_thread, n);
+}
+
+void
+Thread::unblockPollSocket()
+{
+ ARCH->unblockPollSocket(m_thread);
+}
+
+Thread
+Thread::getCurrentThread()
+{
+ return Thread(ARCH->newCurrentThread());
+}
+
+void
+Thread::testCancel()
+{
+ ARCH->testCancelThread();
+}
+
+bool
+Thread::wait(double timeout) const
+{
+ return ARCH->wait(m_thread, timeout);
+}
+
+void*
+Thread::getResult() const
+{
+ if (wait())
+ return ARCH->getResultOfThread(m_thread);
+ else
+ return NULL;
+}
+
+IArchMultithread::ThreadID
+Thread::getID() const
+{
+ return ARCH->getIDOfThread(m_thread);
+}
+
+bool
+Thread::operator==(const Thread& thread) const
+{
+ return ARCH->isSameThread(m_thread, thread.m_thread);
+}
+
+bool
+Thread::operator!=(const Thread& thread) const
+{
+ return !ARCH->isSameThread(m_thread, thread.m_thread);
+}
+
+void*
+Thread::threadFunc(void* vjob)
+{
+ // get this thread's id for logging
+ IArchMultithread::ThreadID id;
+ {
+ ArchThread thread = ARCH->newCurrentThread();
+ id = ARCH->getIDOfThread(thread);
+ ARCH->closeThread(thread);
+ }
+
+ // get job
+ IJob* job = static_cast<IJob*>(vjob);
+
+ // run job
+ void* result = NULL;
+ try {
+ // go
+ LOG((CLOG_DEBUG1 "thread 0x%08x entry", id));
+ job->run();
+ LOG((CLOG_DEBUG1 "thread 0x%08x exit", id));
+ }
+ catch (XThreadCancel&) {
+ // client called cancel()
+ LOG((CLOG_DEBUG1 "caught cancel on thread 0x%08x", id));
+ delete job;
+ throw;
+ }
+ catch (XThreadExit& e) {
+ // client called exit()
+ result = e.m_result;
+ LOG((CLOG_DEBUG1 "caught exit on thread 0x%08x, result %p", id, result));
+ }
+ catch (XBase& e) {
+ LOG((CLOG_ERR "exception on thread 0x%08x: %s", id, e.what()));
+ delete job;
+ throw;
+ }
+ catch (...) {
+ LOG((CLOG_ERR "exception on thread 0x%08x: <unknown>", id));
+ delete job;
+ throw;
+ }
+
+ // done with job
+ delete job;
+
+ // return exit result
+ return result;
+}
diff --git a/src/lib/mt/Thread.h b/src/lib/mt/Thread.h
new file mode 100644
index 0000000..a7434fd
--- /dev/null
+++ b/src/lib/mt/Thread.h
@@ -0,0 +1,210 @@
+/*
+ * barrier -- 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 "arch/IArchMultithread.h"
+
+class IJob;
+
+//! Thread handle
+/*!
+Creating a Thread creates a new context of execution (i.e. thread) that
+runs simulatenously with the calling thread. A Thread is only a handle
+to a thread; deleting a Thread does not cancel or destroy the thread it
+refers to and multiple Thread objects can refer to the same thread.
+
+Threads can terminate themselves but cannot be forced to terminate by
+other threads. However, other threads can signal a thread to terminate
+itself by cancelling it. And a thread can wait (block) on another thread
+to terminate.
+
+Most functions that can block for an arbitrary time are cancellation
+points. A cancellation point is a function that can be interrupted by
+a request to cancel the thread. Cancellation points are noted in the
+documentation.
+*/
+// note -- do not derive from this class
+class Thread {
+public:
+ //! Run \c adoptedJob in a new thread
+ /*!
+ Create and start a new thread executing the \c adoptedJob. The
+ new thread takes ownership of \c adoptedJob and will delete it.
+ */
+ Thread(IJob* adoptedJob);
+
+ //! Duplicate a thread handle
+ /*!
+ Make a new thread object that refers to an existing thread.
+ This does \b not start a new thread.
+ */
+ Thread(const Thread&);
+
+ //! Release a thread handle
+ /*!
+ Release a thread handle. This does not terminate the thread. A thread
+ will keep running until the job completes or calls exit() or allows
+ itself to be cancelled.
+ */
+ ~Thread();
+
+ //! @name manipulators
+ //@{
+
+ //! Assign thread handle
+ /*!
+ Assign a thread handle. This has no effect on the threads, it simply
+ makes this thread object refer to another thread. It does \b not
+ start a new thread.
+ */
+ Thread& operator=(const Thread&);
+
+ //! Terminate the calling thread
+ /*!
+ Terminate the calling thread. This function does not return but
+ the stack is unwound and automatic objects are destroyed, as if
+ exit() threw an exception (which is, in fact, what it does). The
+ argument is saved as the result returned by getResult(). If you
+ have \c catch(...) blocks then you should add the following before
+ each to avoid catching the exit:
+ \code
+ catch(ThreadExit&) { throw; }
+ \endcode
+ or add the \c RETHROW_XTHREAD macro to the \c catch(...) block.
+ */
+ static void exit(void*);
+
+ //! Cancel thread
+ /*!
+ Cancel the thread. cancel() never waits for the thread to
+ terminate; it just posts the cancel and returns. A thread will
+ terminate when it enters a cancellation point with cancellation
+ enabled. If cancellation is disabled then the cancel is
+ remembered but not acted on until the first call to a
+ cancellation point after cancellation is enabled.
+
+ A cancellation point is a function that can act on cancellation.
+ A cancellation point does not return if there's a cancel pending.
+ Instead, it unwinds the stack and destroys automatic objects, as
+ if cancel() threw an exception (which is, in fact, what it does).
+ Threads must take care to unlock and clean up any resources they
+ may have, especially mutexes. They can \c catch(XThreadCancel) to
+ do that then rethrow the exception or they can let it happen
+ automatically by doing clean up in the d'tors of automatic
+ objects (like Lock). Clients are strongly encouraged to do the latter.
+ During cancellation, further cancel() calls are ignored (i.e.
+ a thread cannot be interrupted by a cancel during cancellation).
+
+ Clients that \c catch(XThreadCancel) must always rethrow the
+ exception. Clients that \c catch(...) must either rethrow the
+ exception or include a \c catch(XThreadCancel) handler that
+ rethrows. The \c RETHROW_XTHREAD macro may be useful for that.
+ */
+ void cancel();
+
+ //! Change thread priority
+ /*!
+ Change the priority of the thread. Normal priority is 0, 1 is
+ the next lower, etc. -1 is the next higher, etc. but boosting
+ the priority may not be permitted and will be silenty ignored.
+ */
+ void setPriority(int n);
+
+ //! Force pollSocket() to return
+ /*!
+ Forces a currently blocked pollSocket() in the thread to return
+ immediately.
+ */
+ void unblockPollSocket();
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get current thread's handle
+ /*!
+ Return a Thread object representing the calling thread.
+ */
+ static Thread getCurrentThread();
+
+ //! Test for cancellation
+ /*!
+ testCancel() does nothing but is a cancellation point. Call
+ this to make a function itself a cancellation point. If the
+ thread was cancelled and cancellation is enabled this will
+ cause the thread to unwind the stack and terminate.
+
+ (cancellation point)
+ */
+ static void testCancel();
+
+ //! Wait for thread to terminate
+ /*!
+ Waits for the thread to terminate (by exit() or cancel() or
+ by returning from the thread job) for up to \c timeout seconds,
+ returning true if the thread terminated and false otherwise.
+ This returns immediately with false if called by a thread on
+ itself and immediately with true if the thread has already
+ terminated. This will wait forever if \c timeout < 0.0.
+
+ (cancellation point)
+ */
+ bool wait(double timeout = -1.0) const;
+
+ //! Get the exit result
+ /*!
+ Returns the exit result. This does an implicit wait(). It returns
+ NULL immediately if called by a thread on itself or on a thread that
+ was cancelled.
+
+ (cancellation point)
+ */
+ void* getResult() const;
+
+ //! Get the thread id
+ /*!
+ Returns an integer id for this thread. This id must not be used to
+ check if two Thread objects refer to the same thread. Use
+ operator==() for that.
+ */
+ IArchMultithread::ThreadID
+ getID() const;
+
+ //! Compare thread handles
+ /*!
+ Returns true if two Thread objects refer to the same thread.
+ */
+ bool operator==(const Thread&) const;
+
+ //! Compare thread handles
+ /*!
+ Returns true if two Thread objects do not refer to the same thread.
+ */
+ bool operator!=(const Thread&) const;
+
+ //@}
+
+private:
+ Thread(ArchThread);
+
+ static void* threadFunc(void*);
+
+private:
+ ArchThread m_thread;
+};
diff --git a/src/lib/mt/XMT.cpp b/src/lib/mt/XMT.cpp
new file mode 100644
index 0000000..9aa5852
--- /dev/null
+++ b/src/lib/mt/XMT.cpp
@@ -0,0 +1,29 @@
+/*
+ * barrier -- 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 "XMT.h"
+
+//
+// XMTThreadUnavailable
+//
+
+String
+XMTThreadUnavailable::getWhat() const throw()
+{
+ return format("XMTThreadUnavailable", "cannot create thread");
+}
diff --git a/src/lib/mt/XMT.h b/src/lib/mt/XMT.h
new file mode 100644
index 0000000..9e48fd9
--- /dev/null
+++ b/src/lib/mt/XMT.h
@@ -0,0 +1,30 @@
+/*
+ * barrier -- 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/XBase.h"
+
+//! Generic multithreading exception
+XBASE_SUBCLASS(XMT, XBase);
+
+//! Thread creation exception
+/*!
+Thrown when a thread cannot be created.
+*/
+XBASE_SUBCLASS_WHAT(XMTThreadUnavailable, XMT);
diff --git a/src/lib/mt/XThread.h b/src/lib/mt/XThread.h
new file mode 100644
index 0000000..acc32e3
--- /dev/null
+++ b/src/lib/mt/XThread.h
@@ -0,0 +1,37 @@
+/*
+ * barrier -- 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 "arch/XArch.h"
+
+//! Thread exception to exit
+/*!
+Thrown by Thread::exit() to exit a thread. Clients of Thread
+must not throw this type but must rethrow it if caught (by
+XThreadExit, XThread, or ...).
+*/
+class XThreadExit : public XThread {
+public:
+ //! \c result is the result of the thread
+ XThreadExit(void* result) : m_result(result) { }
+ ~XThreadExit() { }
+
+public:
+ void* m_result;
+};
diff --git a/src/lib/net/CMakeLists.txt b/src/lib/net/CMakeLists.txt
new file mode 100644
index 0000000..5439450
--- /dev/null
+++ b/src/lib/net/CMakeLists.txt
@@ -0,0 +1,28 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(net STATIC ${sources})
+
+if (UNIX)
+ target_link_libraries(net mt io ${OPENSSL_LIBS})
+endif()
diff --git a/src/lib/net/IDataSocket.cpp b/src/lib/net/IDataSocket.cpp
new file mode 100644
index 0000000..cc679c3
--- /dev/null
+++ b/src/lib/net/IDataSocket.cpp
@@ -0,0 +1,39 @@
+/*
+ * barrier -- 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 "net/IDataSocket.h"
+#include "base/EventQueue.h"
+
+//
+// IDataSocket
+//
+
+void
+IDataSocket::close()
+{
+ // this is here to work around a VC++6 bug. see the header file.
+ assert(0 && "bad call");
+}
+
+void*
+IDataSocket::getEventTarget() const
+{
+ // this is here to work around a VC++6 bug. see the header file.
+ assert(0 && "bad call");
+ return NULL;
+}
diff --git a/src/lib/net/IDataSocket.h b/src/lib/net/IDataSocket.h
new file mode 100644
index 0000000..dc07df5
--- /dev/null
+++ b/src/lib/net/IDataSocket.h
@@ -0,0 +1,73 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "net/ISocket.h"
+#include "io/IStream.h"
+#include "base/String.h"
+#include "base/EventTypes.h"
+
+//! Data stream socket interface
+/*!
+This interface defines the methods common to all network sockets that
+represent a full-duplex data stream.
+*/
+class IDataSocket : public ISocket, public barrier::IStream {
+public:
+ class ConnectionFailedInfo {
+ public:
+ ConnectionFailedInfo(const char* what) : m_what(what) { }
+ String m_what;
+ };
+
+ IDataSocket(IEventQueue* events) { }
+
+ //! @name manipulators
+ //@{
+
+ //! Connect socket
+ /*!
+ Attempt to connect to a remote endpoint. This returns immediately
+ and sends a connected event when successful or a connection failed
+ event when it fails. The stream acts as if shutdown for input and
+ output until the stream connects.
+ */
+ virtual void connect(const NetworkAddress&) = 0;
+
+ //@}
+
+ // ISocket overrides
+ // close() and getEventTarget() aren't pure to work around a bug
+ // in VC++6. it claims the methods are unused locals and warns
+ // that it's removing them. it's presumably tickled by inheriting
+ // methods with identical signatures from both superclasses.
+ virtual void bind(const NetworkAddress&) = 0;
+ virtual void close();
+ virtual void* getEventTarget() const;
+
+ // IStream overrides
+ virtual UInt32 read(void* buffer, UInt32 n) = 0;
+ virtual void write(const void* buffer, UInt32 n) = 0;
+ virtual void flush() = 0;
+ virtual void shutdownInput() = 0;
+ virtual void shutdownOutput() = 0;
+ virtual bool isReady() const = 0;
+ virtual bool isFatal() const = 0;
+ virtual UInt32 getSize() const = 0;
+};
diff --git a/src/lib/net/IListenSocket.h b/src/lib/net/IListenSocket.h
new file mode 100644
index 0000000..73dcc6e
--- /dev/null
+++ b/src/lib/net/IListenSocket.h
@@ -0,0 +1,51 @@
+/*
+ * barrier -- 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 "net/ISocket.h"
+#include "base/EventTypes.h"
+
+class IDataSocket;
+
+//! Listen socket interface
+/*!
+This interface defines the methods common to all network sockets that
+listen for incoming connections.
+*/
+class IListenSocket : public ISocket {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Accept connection
+ /*!
+ Accept a connection, returning a socket representing the full-duplex
+ data stream. Returns NULL if no socket is waiting to be accepted.
+ This is only valid after a call to \c bind().
+ */
+ virtual IDataSocket*
+ accept() = 0;
+
+ //@}
+
+ // ISocket overrides
+ virtual void bind(const NetworkAddress&) = 0;
+ virtual void close() = 0;
+ virtual void* getEventTarget() const = 0;
+};
diff --git a/src/lib/net/ISocket.h b/src/lib/net/ISocket.h
new file mode 100644
index 0000000..0e9688b
--- /dev/null
+++ b/src/lib/net/ISocket.h
@@ -0,0 +1,60 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+#include "base/Event.h"
+#include "base/EventTypes.h"
+
+class NetworkAddress;
+
+//! Generic socket interface
+/*!
+This interface defines the methods common to all network sockets.
+Generated events use \c this as the target.
+*/
+class ISocket : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Bind socket to address
+ /*!
+ Binds the socket to a particular address.
+ */
+ virtual void bind(const NetworkAddress&) = 0;
+
+ //! Close socket
+ /*!
+ Closes the socket. This should flush the output stream.
+ */
+ virtual void close() = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get event target
+ /*!
+ Returns the event target for events generated by this socket.
+ */
+ virtual void* getEventTarget() const = 0;
+
+ //@}
+};
diff --git a/src/lib/net/ISocketFactory.h b/src/lib/net/ISocketFactory.h
new file mode 100644
index 0000000..e440953
--- /dev/null
+++ b/src/lib/net/ISocketFactory.h
@@ -0,0 +1,48 @@
+/*
+ * barrier -- 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 "common/IInterface.h"
+#include "arch/IArchNetwork.h"
+
+class IDataSocket;
+class IListenSocket;
+
+//! Socket factory
+/*!
+This interface defines the methods common to all factories used to
+create sockets.
+*/
+class ISocketFactory : public IInterface {
+public:
+ //! @name accessors
+ //@{
+
+ //! Create data socket
+ virtual IDataSocket* create(
+ IArchNetwork::EAddressFamily family,
+ bool secure) const = 0;
+
+ //! Create listen socket
+ virtual IListenSocket* createListen(
+ IArchNetwork::EAddressFamily family,
+ bool secure) const = 0;
+
+ //@}
+};
diff --git a/src/lib/net/ISocketMultiplexerJob.h b/src/lib/net/ISocketMultiplexerJob.h
new file mode 100644
index 0000000..ddd3ba5
--- /dev/null
+++ b/src/lib/net/ISocketMultiplexerJob.h
@@ -0,0 +1,76 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "arch/IArchNetwork.h"
+#include "common/IInterface.h"
+
+//! Socket multiplexer job
+/*!
+A socket multiplexer job handles events on a socket.
+*/
+class ISocketMultiplexerJob : public IInterface {
+public:
+ //! @name manipulators
+ //@{
+
+ //! Handle socket event
+ /*!
+ Called by a socket multiplexer when the socket becomes readable,
+ writable, or has an error. It should return itself if the same
+ job can continue to service events, a new job if the socket must
+ be serviced differently, or NULL if the socket should no longer
+ be serviced. The socket is readable if \p readable is true,
+ writable if \p writable is true, and in error if \p error is
+ true.
+
+ This call must not attempt to directly change the job for this
+ socket by calling \c addSocket() or \c removeSocket() on the
+ multiplexer. It must instead return the new job. It can,
+ however, add or remove jobs for other sockets.
+ */
+ virtual ISocketMultiplexerJob*
+ run(bool readable, bool writable, bool error) = 0;
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get the socket
+ /*!
+ Return the socket to multiplex
+ */
+ virtual ArchSocket getSocket() const = 0;
+
+ //! Check for interest in readability
+ /*!
+ Return true if the job is interested in being run if the socket
+ becomes readable.
+ */
+ virtual bool isReadable() const = 0;
+
+ //! Check for interest in writability
+ /*!
+ Return true if the job is interested in being run if the socket
+ becomes writable.
+ */
+ virtual bool isWritable() const = 0;
+
+ //@}
+};
diff --git a/src/lib/net/NetworkAddress.cpp b/src/lib/net/NetworkAddress.cpp
new file mode 100644
index 0000000..c395ab0
--- /dev/null
+++ b/src/lib/net/NetworkAddress.cpp
@@ -0,0 +1,214 @@
+/*
+ * barrier -- 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 "net/NetworkAddress.h"
+
+#include "net/XSocket.h"
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+
+#include <cstdlib>
+
+//
+// NetworkAddress
+//
+
+// name re-resolution adapted from a patch by Brent Priddy.
+
+NetworkAddress::NetworkAddress() :
+ m_address(NULL),
+ m_hostname(),
+ m_port(0)
+{
+ // note -- make no calls to Network socket interface here;
+ // we're often called prior to Network::init().
+}
+
+NetworkAddress::NetworkAddress(int port) :
+ m_address(NULL),
+ m_hostname(),
+ m_port(port)
+{
+ checkPort();
+ m_address = ARCH->newAnyAddr(IArchNetwork::kINET);
+ ARCH->setAddrPort(m_address, m_port);
+}
+
+NetworkAddress::NetworkAddress(const NetworkAddress& addr) :
+ m_address(addr.m_address != NULL ? ARCH->copyAddr(addr.m_address) : NULL),
+ m_hostname(addr.m_hostname),
+ m_port(addr.m_port)
+{
+ // do nothing
+}
+
+NetworkAddress::NetworkAddress(const String& hostname, int port) :
+ m_address(NULL),
+ m_hostname(hostname),
+ m_port(port)
+{
+ // check for port suffix
+ String::size_type i = m_hostname.rfind(':');
+ if (i != String::npos && i + 1 < m_hostname.size()) {
+ // found a colon. see if it looks like an IPv6 address.
+ bool colonNotation = false;
+ bool dotNotation = false;
+ bool doubleColon = false;
+ for (String::size_type j = 0; j < i; ++j) {
+ if (m_hostname[j] == ':') {
+ colonNotation = true;
+ dotNotation = false;
+ if (m_hostname[j + 1] == ':') {
+ doubleColon = true;
+ }
+ }
+ else if (m_hostname[j] == '.' && colonNotation) {
+ dotNotation = true;
+ }
+ }
+
+ // port suffix is ambiguous with IPv6 notation if there's
+ // a double colon and the end of the address is not in dot
+ // notation. in that case we assume it's not a port suffix.
+ // the user can replace the double colon with zeros to
+ // disambiguate.
+ if ((!doubleColon || dotNotation) && !colonNotation) {
+ // parse port from hostname
+ char* end;
+ const char* chostname = m_hostname.c_str();
+ long suffixPort = strtol(chostname + i + 1, &end, 10);
+ if (end == chostname + i + 1 || *end != '\0') {
+ throw XSocketAddress(XSocketAddress::kBadPort,
+ m_hostname, m_port);
+ }
+
+ // trim port from hostname
+ m_hostname.erase(i);
+
+ // save port
+ m_port = static_cast<int>(suffixPort);
+ }
+ }
+
+ // check port number
+ checkPort();
+}
+
+NetworkAddress::~NetworkAddress()
+{
+ if (m_address != NULL) {
+ ARCH->closeAddr(m_address);
+ }
+}
+
+NetworkAddress&
+NetworkAddress::operator=(const NetworkAddress& addr)
+{
+ ArchNetAddress newAddr = NULL;
+ if (addr.m_address != NULL) {
+ newAddr = ARCH->copyAddr(addr.m_address);
+ }
+ if (m_address != NULL) {
+ ARCH->closeAddr(m_address);
+ }
+ m_address = newAddr;
+ m_hostname = addr.m_hostname;
+ m_port = addr.m_port;
+ return *this;
+}
+
+void
+NetworkAddress::resolve()
+{
+ // discard previous address
+ if (m_address != NULL) {
+ ARCH->closeAddr(m_address);
+ m_address = NULL;
+ }
+
+ try {
+ // if hostname is empty then use wildcard address otherwise look
+ // up the name.
+ if (m_hostname.empty()) {
+ m_address = ARCH->newAnyAddr(IArchNetwork::kINET);
+ }
+ else {
+ m_address = ARCH->nameToAddr(m_hostname);
+ }
+ }
+ catch (XArchNetworkNameUnknown&) {
+ throw XSocketAddress(XSocketAddress::kNotFound, m_hostname, m_port);
+ }
+ catch (XArchNetworkNameNoAddress&) {
+ throw XSocketAddress(XSocketAddress::kNoAddress, m_hostname, m_port);
+ }
+ catch (XArchNetworkNameUnsupported&) {
+ throw XSocketAddress(XSocketAddress::kUnsupported, m_hostname, m_port);
+ }
+ catch (XArchNetworkName&) {
+ throw XSocketAddress(XSocketAddress::kUnknown, m_hostname, m_port);
+ }
+
+ // set port in address
+ ARCH->setAddrPort(m_address, m_port);
+}
+
+bool
+NetworkAddress::operator==(const NetworkAddress& addr) const
+{
+ return ARCH->isEqualAddr(m_address, addr.m_address);
+}
+
+bool
+NetworkAddress::operator!=(const NetworkAddress& addr) const
+{
+ return !operator==(addr);
+}
+
+bool
+NetworkAddress::isValid() const
+{
+ return (m_address != NULL);
+}
+
+const ArchNetAddress&
+NetworkAddress::getAddress() const
+{
+ return m_address;
+}
+
+int
+NetworkAddress::getPort() const
+{
+ return m_port;
+}
+
+String
+NetworkAddress::getHostname() const
+{
+ return m_hostname;
+}
+
+void
+NetworkAddress::checkPort()
+{
+ // check port number
+ if (m_port <= 0 || m_port > 65535) {
+ throw XSocketAddress(XSocketAddress::kBadPort, m_hostname, m_port);
+ }
+}
diff --git a/src/lib/net/NetworkAddress.h b/src/lib/net/NetworkAddress.h
new file mode 100644
index 0000000..cbd15f5
--- /dev/null
+++ b/src/lib/net/NetworkAddress.h
@@ -0,0 +1,123 @@
+/*
+ * barrier -- 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"
+#include "base/EventTypes.h"
+#include "arch/IArchNetwork.h"
+
+//! Network address type
+/*!
+This class represents a network address.
+*/
+class NetworkAddress {
+public:
+ /*!
+ Constructs the invalid address
+ */
+ NetworkAddress();
+
+ /*!
+ Construct the wildcard address with the given port. \c port must
+ not be zero.
+ */
+ NetworkAddress(int port);
+
+ /*!
+ Construct the network address for the given \c hostname and \c port.
+ If \c hostname can be parsed as a numerical address then that's how
+ it's used, otherwise it's used as a host name. If \c hostname ends
+ in ":[0-9]+" then that suffix is extracted and used as the port,
+ overridding the port parameter. The resulting port must be a valid
+ port number (zero is not a valid port number) otherwise \c XSocketAddress
+ is thrown with an error of \c XSocketAddress::kBadPort. The hostname
+ is not resolved by the c'tor; use \c resolve to do that.
+ */
+ NetworkAddress(const String& hostname, int port);
+
+ NetworkAddress(const NetworkAddress&);
+
+ ~NetworkAddress();
+
+ NetworkAddress& operator=(const NetworkAddress&);
+
+ //! @name manipulators
+ //@{
+
+ //! Resolve address
+ /*!
+ Resolves the hostname to an address. This can be done any number of
+ times and is done automatically by the c'tor taking a hostname.
+ Throws XSocketAddress if resolution is unsuccessful, after which
+ \c isValid returns false until the next call to this method.
+ */
+ void resolve();
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Check address equality
+ /*!
+ Returns true if this address is equal to \p address.
+ */
+ bool operator==(const NetworkAddress& address) const;
+
+ //! Check address inequality
+ /*!
+ Returns true if this address is not equal to \p address.
+ */
+ bool operator!=(const NetworkAddress& address) const;
+
+ //! Check address validity
+ /*!
+ Returns true if this is not the invalid address.
+ */
+ bool isValid() const;
+
+ //! Get address
+ /*!
+ Returns the address in the platform's native network address
+ structure.
+ */
+ const ArchNetAddress& getAddress() const;
+
+ //! Get port
+ /*!
+ Returns the port passed to the c'tor as a suffix to the hostname,
+ if that existed, otherwise as passed directly to the c'tor.
+ */
+ int getPort() const;
+
+ //! Get hostname
+ /*!
+ Returns the hostname passed to the c'tor sans any port suffix.
+ */
+ String getHostname() const;
+
+ //@}
+
+private:
+ void checkPort();
+
+private:
+ ArchNetAddress m_address;
+ String m_hostname;
+ int m_port;
+};
diff --git a/src/lib/net/SecureListenSocket.cpp b/src/lib/net/SecureListenSocket.cpp
new file mode 100644
index 0000000..58ffe09
--- /dev/null
+++ b/src/lib/net/SecureListenSocket.cpp
@@ -0,0 +1,85 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "SecureListenSocket.h"
+
+#include "SecureSocket.h"
+#include "net/NetworkAddress.h"
+#include "net/SocketMultiplexer.h"
+#include "net/TSocketMultiplexerMethodJob.h"
+#include "arch/XArch.h"
+
+static const char s_certificateDir[] = { "SSL" };
+static const char s_certificateFilename[] = { "Barrier.pem" };
+
+//
+// SecureListenSocket
+//
+
+SecureListenSocket::SecureListenSocket(
+ IEventQueue* events,
+ SocketMultiplexer* socketMultiplexer,
+ IArchNetwork::EAddressFamily family) :
+ TCPListenSocket(events, socketMultiplexer, family)
+{
+}
+
+IDataSocket*
+SecureListenSocket::accept()
+{
+ SecureSocket* socket = NULL;
+ try {
+ socket = new SecureSocket(
+ m_events,
+ m_socketMultiplexer,
+ ARCH->acceptSocket(m_socket, NULL));
+ socket->initSsl(true);
+
+ if (socket != NULL) {
+ setListeningJob();
+ }
+
+ String certificateFilename = barrier::string::sprintf("%s/%s/%s",
+ ARCH->getProfileDirectory().c_str(),
+ s_certificateDir,
+ s_certificateFilename);
+
+ bool loaded = socket->loadCertificates(certificateFilename);
+ if (!loaded) {
+ delete socket;
+ return NULL;
+ }
+
+ socket->secureAccept();
+
+ return dynamic_cast<IDataSocket*>(socket);
+ }
+ catch (XArchNetwork&) {
+ if (socket != NULL) {
+ delete socket;
+ setListeningJob();
+ }
+ return NULL;
+ }
+ catch (std::exception &ex) {
+ if (socket != NULL) {
+ delete socket;
+ setListeningJob();
+ }
+ throw ex;
+ }
+}
diff --git a/src/lib/net/SecureListenSocket.h b/src/lib/net/SecureListenSocket.h
new file mode 100644
index 0000000..d0c6e23
--- /dev/null
+++ b/src/lib/net/SecureListenSocket.h
@@ -0,0 +1,36 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+#include "net/TCPListenSocket.h"
+#include "common/stdset.h"
+
+class IEventQueue;
+class SocketMultiplexer;
+class IDataSocket;
+
+class SecureListenSocket : public TCPListenSocket{
+public:
+ SecureListenSocket(IEventQueue* events,
+ SocketMultiplexer* socketMultiplexer,
+ IArchNetwork::EAddressFamily family);
+
+ // IListenSocket overrides
+ virtual IDataSocket*
+ accept();
+};
diff --git a/src/lib/net/SecureSocket.cpp b/src/lib/net/SecureSocket.cpp
new file mode 100644
index 0000000..1fefae0
--- /dev/null
+++ b/src/lib/net/SecureSocket.cpp
@@ -0,0 +1,867 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "SecureSocket.h"
+
+#include "net/TSocketMultiplexerMethodJob.h"
+#include "base/TMethodEventJob.h"
+#include "net/TCPSocket.h"
+#include "mt/Lock.h"
+#include "arch/XArch.h"
+#include "base/Log.h"
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <cstring>
+#include <cstdlib>
+#include <memory>
+#include <fstream>
+#include <memory>
+
+//
+// SecureSocket
+//
+
+#define MAX_ERROR_SIZE 65535
+
+static const float s_retryDelay = 0.01f;
+
+enum {
+ kMsgSize = 128
+};
+
+static const char kFingerprintDirName[] = "SSL/Fingerprints";
+//static const char kFingerprintLocalFilename[] = "Local.txt";
+static const char kFingerprintTrustedServersFilename[] = "TrustedServers.txt";
+//static const char kFingerprintTrustedClientsFilename[] = "TrustedClients.txt";
+
+struct Ssl {
+ SSL_CTX* m_context;
+ SSL* m_ssl;
+};
+
+SecureSocket::SecureSocket(
+ IEventQueue* events,
+ SocketMultiplexer* socketMultiplexer,
+ IArchNetwork::EAddressFamily family) :
+ TCPSocket(events, socketMultiplexer, family),
+ m_ssl(nullptr),
+ m_secureReady(false),
+ m_fatal(false)
+{
+}
+
+SecureSocket::SecureSocket(
+ IEventQueue* events,
+ SocketMultiplexer* socketMultiplexer,
+ ArchSocket socket) :
+ TCPSocket(events, socketMultiplexer, socket),
+ m_ssl(nullptr),
+ m_secureReady(false),
+ m_fatal(false)
+{
+}
+
+SecureSocket::~SecureSocket()
+{
+ isFatal(true);
+ // take socket from multiplexer ASAP otherwise the race condition
+ // could cause events to get called on a dead object. TCPSocket
+ // will do this, too, but the double-call is harmless
+ setJob(NULL);
+ if (m_ssl->m_ssl != NULL) {
+ SSL_shutdown(m_ssl->m_ssl);
+
+ SSL_free(m_ssl->m_ssl);
+ m_ssl->m_ssl = NULL;
+ }
+ if (m_ssl->m_context != NULL) {
+ SSL_CTX_free(m_ssl->m_context);
+ m_ssl->m_context = NULL;
+ }
+ // removing sleep() because I have no idea why you would want to do it
+ // ... smells of trying to cover up a bug you don't understand
+ //ARCH->sleep(1);
+ delete m_ssl;
+}
+
+void
+SecureSocket::close()
+{
+ isFatal(true);
+
+ SSL_shutdown(m_ssl->m_ssl);
+
+ TCPSocket::close();
+}
+
+void
+SecureSocket::connect(const NetworkAddress& addr)
+{
+ m_events->adoptHandler(m_events->forIDataSocket().connected(),
+ getEventTarget(),
+ new TMethodEventJob<SecureSocket>(this,
+ &SecureSocket::handleTCPConnected));
+
+ TCPSocket::connect(addr);
+}
+
+ISocketMultiplexerJob*
+SecureSocket::newJob()
+{
+ // after TCP connection is established, SecureSocket will pick up
+ // connected event and do secureConnect
+ if (m_connected && !m_secureReady) {
+ return NULL;
+ }
+
+ return TCPSocket::newJob();
+}
+
+void
+SecureSocket::secureConnect()
+{
+ setJob(new TSocketMultiplexerMethodJob<SecureSocket>(
+ this, &SecureSocket::serviceConnect,
+ getSocket(), isReadable(), isWritable()));
+}
+
+void
+SecureSocket::secureAccept()
+{
+ setJob(new TSocketMultiplexerMethodJob<SecureSocket>(
+ this, &SecureSocket::serviceAccept,
+ getSocket(), isReadable(), isWritable()));
+}
+
+TCPSocket::EJobResult
+SecureSocket::doRead()
+{
+ static UInt8 buffer[4096];
+ memset(buffer, 0, sizeof(buffer));
+ int bytesRead = 0;
+ int status = 0;
+
+ if (isSecureReady()) {
+ status = secureRead(buffer, sizeof(buffer), bytesRead);
+ if (status < 0) {
+ return kBreak;
+ }
+ else if (status == 0) {
+ return kNew;
+ }
+ }
+ else {
+ return kRetry;
+ }
+
+ if (bytesRead > 0) {
+ bool wasEmpty = (m_inputBuffer.getSize() == 0);
+
+ // slurp up as much as possible
+ do {
+ m_inputBuffer.write(buffer, bytesRead);
+
+ status = secureRead(buffer, sizeof(buffer), bytesRead);
+ if (status < 0) {
+ return kBreak;
+ }
+ } while (bytesRead > 0 || status > 0);
+
+ // send input ready if input buffer was empty
+ if (wasEmpty) {
+ sendEvent(m_events->forIStream().inputReady());
+ }
+ }
+ else {
+ // remote write end of stream hungup. our input side
+ // has therefore shutdown but don't flush our buffer
+ // since there's still data to be read.
+ sendEvent(m_events->forIStream().inputShutdown());
+ if (!m_writable && m_inputBuffer.getSize() == 0) {
+ sendEvent(m_events->forISocket().disconnected());
+ m_connected = false;
+ }
+ m_readable = false;
+ return kNew;
+ }
+
+ return kRetry;
+}
+
+TCPSocket::EJobResult
+SecureSocket::doWrite()
+{
+ static bool s_retry = false;
+ static int s_retrySize = 0;
+ static std::unique_ptr<char[]> s_staticBuffer;
+ static std::size_t s_staticBufferSize = 0;
+
+ // write data
+ int bufferSize = 0;
+ int bytesWrote = 0;
+ int status = 0;
+
+ if (!isSecureReady())
+ return kRetry;
+
+ if (s_retry) {
+ bufferSize = s_retrySize;
+ } else {
+ bufferSize = m_outputBuffer.getSize();
+ if (bufferSize > s_staticBufferSize) {
+ s_staticBuffer.reset(new char[bufferSize]);
+ s_staticBufferSize = bufferSize;
+ }
+ if (bufferSize > 0) {
+ memcpy(s_staticBuffer.get(), m_outputBuffer.peek(bufferSize), bufferSize);
+ }
+ }
+
+ if (bufferSize == 0) {
+ return kRetry;
+ }
+
+ status = secureWrite(s_staticBuffer.get(), bufferSize, bytesWrote);
+ if (status > 0) {
+ s_retry = false;
+ } else if (status < 0) {
+ return kBreak;
+ } else if (status == 0) {
+ s_retry = true;
+ s_retrySize = bufferSize;
+ return kNew;
+ }
+
+ if (bytesWrote > 0) {
+ discardWrittenData(bytesWrote);
+ return kNew;
+ }
+
+ return kRetry;
+}
+
+int
+SecureSocket::secureRead(void* buffer, int size, int& read)
+{
+ if (m_ssl->m_ssl != NULL) {
+ LOG((CLOG_DEBUG2 "reading secure socket"));
+ read = SSL_read(m_ssl->m_ssl, buffer, size);
+
+ static int retry;
+
+ // Check result will cleanup the connection in the case of a fatal
+ checkResult(read, retry);
+
+ if (retry) {
+ return 0;
+ }
+
+ if (isFatal()) {
+ return -1;
+ }
+ }
+ // According to SSL spec, the number of bytes read must not be negative and
+ // not have an error code from SSL_get_error(). If this happens, it is
+ // itself an error. Let the parent handle the case
+ return read;
+}
+
+int
+SecureSocket::secureWrite(const void* buffer, int size, int& wrote)
+{
+ if (m_ssl->m_ssl != NULL) {
+ LOG((CLOG_DEBUG2 "writing secure socket:%p", this));
+
+ wrote = SSL_write(m_ssl->m_ssl, buffer, size);
+
+ static int retry;
+
+ // Check result will cleanup the connection in the case of a fatal
+ checkResult(wrote, retry);
+
+ if (retry) {
+ return 0;
+ }
+
+ if (isFatal()) {
+ return -1;
+ }
+ }
+ // According to SSL spec, r must not be negative and not have an error code
+ // from SSL_get_error(). If this happens, it is itself an error. Let the
+ // parent handle the case
+ return wrote;
+}
+
+bool
+SecureSocket::isSecureReady()
+{
+ return m_secureReady;
+}
+
+void
+SecureSocket::initSsl(bool server)
+{
+ m_ssl = new Ssl();
+ m_ssl->m_context = NULL;
+ m_ssl->m_ssl = NULL;
+
+ initContext(server);
+}
+
+bool
+SecureSocket::loadCertificates(String& filename)
+{
+ if (filename.empty()) {
+ showError("ssl certificate is not specified");
+ return false;
+ }
+ else {
+ std::ifstream file(filename.c_str());
+ bool exist = file.good();
+ file.close();
+
+ if (!exist) {
+ String errorMsg("ssl certificate doesn't exist: ");
+ errorMsg.append(filename);
+ showError(errorMsg.c_str());
+ return false;
+ }
+ }
+
+ int r = 0;
+ r = SSL_CTX_use_certificate_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM);
+ if (r <= 0) {
+ showError("could not use ssl certificate");
+ return false;
+ }
+
+ r = SSL_CTX_use_PrivateKey_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM);
+ if (r <= 0) {
+ showError("could not use ssl private key");
+ return false;
+ }
+
+ r = SSL_CTX_check_private_key(m_ssl->m_context);
+ if (!r) {
+ showError("could not verify ssl private key");
+ return false;
+ }
+
+ return true;
+}
+
+void
+SecureSocket::initContext(bool server)
+{
+ SSL_library_init();
+
+ const SSL_METHOD* method;
+
+ // load & register all cryptos, etc.
+ OpenSSL_add_all_algorithms();
+
+ // load all error messages
+ SSL_load_error_strings();
+
+ if (CLOG->getFilter() >= kINFO) {
+ showSecureLibInfo();
+ }
+
+ // SSLv23_method uses TLSv1, with the ability to fall back to SSLv3
+ if (server) {
+ method = SSLv23_server_method();
+ }
+ else {
+ method = SSLv23_client_method();
+ }
+
+ // create new context from method
+ SSL_METHOD* m = const_cast<SSL_METHOD*>(method);
+ m_ssl->m_context = SSL_CTX_new(m);
+
+ // drop SSLv3 support
+ SSL_CTX_set_options(m_ssl->m_context, SSL_OP_NO_SSLv3);
+
+ if (m_ssl->m_context == NULL) {
+ showError();
+ }
+}
+
+void
+SecureSocket::createSSL()
+{
+ // I assume just one instance is needed
+ // get new SSL state with context
+ if (m_ssl->m_ssl == NULL) {
+ assert(m_ssl->m_context != NULL);
+ m_ssl->m_ssl = SSL_new(m_ssl->m_context);
+ }
+}
+
+int
+SecureSocket::secureAccept(int socket)
+{
+ createSSL();
+
+ // set connection socket to SSL state
+ SSL_set_fd(m_ssl->m_ssl, socket);
+
+ LOG((CLOG_DEBUG2 "accepting secure socket"));
+ int r = SSL_accept(m_ssl->m_ssl);
+
+ static int retry;
+
+ checkResult(r, retry);
+
+ if (isFatal()) {
+ // tell user and sleep so the socket isn't hammered.
+ LOG((CLOG_ERR "failed to accept secure socket"));
+ LOG((CLOG_INFO "client connection may not be secure"));
+ m_secureReady = false;
+ ARCH->sleep(1);
+ retry = 0;
+ return -1; // Failed, error out
+ }
+
+ // If not fatal and no retry, state is good
+ if (retry == 0) {
+ m_secureReady = true;
+ LOG((CLOG_INFO "accepted secure socket"));
+ if (CLOG->getFilter() >= kDEBUG1) {
+ showSecureCipherInfo();
+ }
+ showSecureConnectInfo();
+ return 1;
+ }
+
+ // If not fatal and retry is set, not ready, and return retry
+ if (retry > 0) {
+ LOG((CLOG_DEBUG2 "retry accepting secure socket"));
+ m_secureReady = false;
+ ARCH->sleep(s_retryDelay);
+ return 0;
+ }
+
+ // no good state exists here
+ LOG((CLOG_ERR "unexpected state attempting to accept connection"));
+ return -1;
+}
+
+int
+SecureSocket::secureConnect(int socket)
+{
+ createSSL();
+
+ // attach the socket descriptor
+ SSL_set_fd(m_ssl->m_ssl, socket);
+
+ LOG((CLOG_DEBUG2 "connecting secure socket"));
+ int r = SSL_connect(m_ssl->m_ssl);
+
+ static int retry;
+
+ checkResult(r, retry);
+
+ if (isFatal()) {
+ LOG((CLOG_ERR "failed to connect secure socket"));
+ retry = 0;
+ return -1;
+ }
+
+ // If we should retry, not ready and return 0
+ if (retry > 0) {
+ LOG((CLOG_DEBUG2 "retry connect secure socket"));
+ m_secureReady = false;
+ ARCH->sleep(s_retryDelay);
+ return 0;
+ }
+
+ retry = 0;
+ // No error, set ready, process and return ok
+ m_secureReady = true;
+ if (verifyCertFingerprint()) {
+ LOG((CLOG_INFO "connected to secure socket"));
+ if (!showCertificate()) {
+ disconnect();
+ return -1;// Cert fail, error
+ }
+ }
+ else {
+ LOG((CLOG_ERR "failed to verify server certificate fingerprint"));
+ disconnect();
+ return -1; // Fingerprint failed, error
+ }
+ LOG((CLOG_DEBUG2 "connected secure socket"));
+ if (CLOG->getFilter() >= kDEBUG1) {
+ showSecureCipherInfo();
+ }
+ showSecureConnectInfo();
+ return 1;
+}
+
+bool
+SecureSocket::showCertificate()
+{
+ X509* cert;
+ char* line;
+
+ // get the server's certificate
+ cert = SSL_get_peer_certificate(m_ssl->m_ssl);
+ if (cert != NULL) {
+ line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
+ LOG((CLOG_INFO "server ssl certificate info: %s", line));
+ OPENSSL_free(line);
+ X509_free(cert);
+ }
+ else {
+ showError("server has no ssl certificate");
+ return false;
+ }
+
+ return true;
+}
+
+void
+SecureSocket::checkResult(int status, int& retry)
+{
+ // ssl errors are a little quirky. the "want" errors are normal and
+ // should result in a retry.
+
+ int errorCode = SSL_get_error(m_ssl->m_ssl, status);
+
+ switch (errorCode) {
+ case SSL_ERROR_NONE:
+ retry = 0;
+ // operation completed
+ break;
+
+ case SSL_ERROR_ZERO_RETURN:
+ // connection closed
+ isFatal(true);
+ LOG((CLOG_DEBUG "ssl connection closed"));
+ break;
+
+ case SSL_ERROR_WANT_READ:
+ retry++;
+ LOG((CLOG_DEBUG2 "want to read, error=%d, attempt=%d", errorCode, retry));
+ break;
+
+ case SSL_ERROR_WANT_WRITE:
+ // Need to make sure the socket is known to be writable so the impending
+ // select action actually triggers on a write. This isn't necessary for
+ // m_readable because the socket logic is always readable
+ m_writable = true;
+ retry++;
+ LOG((CLOG_DEBUG2 "want to write, error=%d, attempt=%d", errorCode, retry));
+ break;
+
+ case SSL_ERROR_WANT_CONNECT:
+ retry++;
+ LOG((CLOG_DEBUG2 "want to connect, error=%d, attempt=%d", errorCode, retry));
+ break;
+
+ case SSL_ERROR_WANT_ACCEPT:
+ retry++;
+ LOG((CLOG_DEBUG2 "want to accept, error=%d, attempt=%d", errorCode, retry));
+ break;
+
+ case SSL_ERROR_SYSCALL:
+ LOG((CLOG_ERR "ssl error occurred (system call failure)"));
+ if (ERR_peek_error() == 0) {
+ if (status == 0) {
+ LOG((CLOG_ERR "eof violates ssl protocol"));
+ }
+ else if (status == -1) {
+ // underlying socket I/O reproted an error
+ try {
+ ARCH->throwErrorOnSocket(getSocket());
+ }
+ catch (XArchNetwork& e) {
+ LOG((CLOG_ERR "%s", e.what()));
+ }
+ }
+ }
+
+ isFatal(true);
+ break;
+
+ case SSL_ERROR_SSL:
+ LOG((CLOG_ERR "ssl error occurred (generic failure)"));
+ isFatal(true);
+ break;
+
+ default:
+ LOG((CLOG_ERR "ssl error occurred (unknown failure)"));
+ isFatal(true);
+ break;
+ }
+
+ if (isFatal()) {
+ retry = 0;
+ showError();
+ disconnect();
+ }
+}
+
+void
+SecureSocket::showError(const char* reason)
+{
+ if (reason != NULL) {
+ LOG((CLOG_ERR "%s", reason));
+ }
+
+ String error = getError();
+ if (!error.empty()) {
+ LOG((CLOG_ERR "%s", error.c_str()));
+ }
+}
+
+String
+SecureSocket::getError()
+{
+ unsigned long e = ERR_get_error();
+
+ if (e != 0) {
+ char error[MAX_ERROR_SIZE];
+ ERR_error_string_n(e, error, MAX_ERROR_SIZE);
+ return error;
+ }
+ else {
+ return "";
+ }
+}
+
+void
+SecureSocket::disconnect()
+{
+ sendEvent(getEvents()->forISocket().stopRetry());
+ sendEvent(getEvents()->forISocket().disconnected());
+ sendEvent(getEvents()->forIStream().inputShutdown());
+}
+
+void
+SecureSocket::formatFingerprint(String& fingerprint, bool hex, bool separator)
+{
+ if (hex) {
+ // to hexidecimal
+ barrier::string::toHex(fingerprint, 2);
+ }
+
+ // all uppercase
+ barrier::string::uppercase(fingerprint);
+
+ if (separator) {
+ // add colon to separate each 2 charactors
+ size_t separators = fingerprint.size() / 2;
+ for (size_t i = 1; i < separators; i++) {
+ fingerprint.insert(i * 3 - 1, ":");
+ }
+ }
+}
+
+bool
+SecureSocket::verifyCertFingerprint()
+{
+ // calculate received certificate fingerprint
+ X509 *cert = cert = SSL_get_peer_certificate(m_ssl->m_ssl);
+ EVP_MD* tempDigest;
+ unsigned char tempFingerprint[EVP_MAX_MD_SIZE];
+ unsigned int tempFingerprintLen;
+ tempDigest = (EVP_MD*)EVP_sha1();
+ int digestResult = X509_digest(cert, tempDigest, tempFingerprint, &tempFingerprintLen);
+
+ if (digestResult <= 0) {
+ LOG((CLOG_ERR "failed to calculate fingerprint, digest result: %d", digestResult));
+ return false;
+ }
+
+ // format fingerprint into hexdecimal format with colon separator
+ String fingerprint(reinterpret_cast<char*>(tempFingerprint), tempFingerprintLen);
+ formatFingerprint(fingerprint);
+ LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str()));
+
+ String trustedServersFilename;
+ trustedServersFilename = barrier::string::sprintf(
+ "%s/%s/%s",
+ ARCH->getProfileDirectory().c_str(),
+ kFingerprintDirName,
+ kFingerprintTrustedServersFilename);
+
+ // check if this fingerprint exist
+ String fileLine;
+ std::ifstream file;
+ file.open(trustedServersFilename.c_str());
+
+ bool isValid = false;
+ while (!file.eof() && file.is_open()) {
+ getline(file,fileLine);
+ if (!fileLine.empty()) {
+ if (fileLine.compare(fingerprint) == 0) {
+ isValid = true;
+ break;
+ }
+ }
+ }
+
+ file.close();
+ return isValid;
+}
+
+ISocketMultiplexerJob*
+SecureSocket::serviceConnect(ISocketMultiplexerJob* job,
+ bool, bool write, bool error)
+{
+ Lock lock(&getMutex());
+
+ int status = 0;
+#ifdef SYSAPI_WIN32
+ status = secureConnect(static_cast<int>(getSocket()->m_socket));
+#elif SYSAPI_UNIX
+ status = secureConnect(getSocket()->m_fd);
+#endif
+
+ // If status < 0, error happened
+ if (status < 0) {
+ return NULL;
+ }
+
+ // If status > 0, success
+ if (status > 0) {
+ sendEvent(m_events->forIDataSocket().secureConnected());
+ return newJob();
+ }
+
+ // Retry case
+ return new TSocketMultiplexerMethodJob<SecureSocket>(
+ this, &SecureSocket::serviceConnect,
+ getSocket(), isReadable(), isWritable());
+}
+
+ISocketMultiplexerJob*
+SecureSocket::serviceAccept(ISocketMultiplexerJob* job,
+ bool, bool write, bool error)
+{
+ Lock lock(&getMutex());
+
+ int status = 0;
+#ifdef SYSAPI_WIN32
+ status = secureAccept(static_cast<int>(getSocket()->m_socket));
+#elif SYSAPI_UNIX
+ status = secureAccept(getSocket()->m_fd);
+#endif
+ // If status < 0, error happened
+ if (status < 0) {
+ return NULL;
+ }
+
+ // If status > 0, success
+ if (status > 0) {
+ sendEvent(m_events->forClientListener().accepted());
+ return newJob();
+ }
+
+ // Retry case
+ return new TSocketMultiplexerMethodJob<SecureSocket>(
+ this, &SecureSocket::serviceAccept,
+ getSocket(), isReadable(), isWritable());
+}
+
+void
+showCipherStackDesc(STACK_OF(SSL_CIPHER) * stack) {
+ char msg[kMsgSize];
+ int i = 0;
+ for ( ; i < sk_SSL_CIPHER_num(stack) ; i++) {
+ const SSL_CIPHER * cipher = sk_SSL_CIPHER_value(stack,i);
+
+ SSL_CIPHER_description(cipher, msg, kMsgSize);
+
+ // Why does SSL put a newline in the description?
+ int pos = (int)strlen(msg) - 1;
+ if (msg[pos] == '\n') {
+ msg[pos] = '\0';
+ }
+
+ LOG((CLOG_DEBUG1 "%s",msg));
+ }
+}
+
+void
+SecureSocket::showSecureCipherInfo()
+{
+ STACK_OF(SSL_CIPHER) * sStack = SSL_get_ciphers(m_ssl->m_ssl);
+
+ if (sStack == NULL) {
+ LOG((CLOG_DEBUG1 "local cipher list not available"));
+ }
+ else {
+ LOG((CLOG_DEBUG1 "available local ciphers:"));
+ showCipherStackDesc(sStack);
+ }
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ // m_ssl->m_ssl->session->ciphers is not forward compatable,
+ // In future release of OpenSSL, it's not visible,
+ STACK_OF(SSL_CIPHER) * cStack = m_ssl->m_ssl->session->ciphers;
+#else
+ // Use SSL_get_client_ciphers() for newer versions
+ STACK_OF(SSL_CIPHER) * cStack = SSL_get_client_ciphers(m_ssl->m_ssl);
+#endif
+ if (cStack == NULL) {
+ LOG((CLOG_DEBUG1 "remote cipher list not available"));
+ }
+ else {
+ LOG((CLOG_DEBUG1 "available remote ciphers:"));
+ showCipherStackDesc(cStack);
+ }
+ return;
+}
+
+void
+SecureSocket::showSecureLibInfo()
+{
+ LOG((CLOG_INFO "%s",SSLeay_version(SSLEAY_VERSION)));
+ LOG((CLOG_DEBUG1 "openSSL : %s",SSLeay_version(SSLEAY_CFLAGS)));
+ LOG((CLOG_DEBUG1 "openSSL : %s",SSLeay_version(SSLEAY_BUILT_ON)));
+ LOG((CLOG_DEBUG1 "openSSL : %s",SSLeay_version(SSLEAY_PLATFORM)));
+ LOG((CLOG_DEBUG1 "%s",SSLeay_version(SSLEAY_DIR)));
+ return;
+}
+
+void
+SecureSocket::showSecureConnectInfo()
+{
+ const SSL_CIPHER* cipher = SSL_get_current_cipher(m_ssl->m_ssl);
+
+ if (cipher != NULL) {
+ char msg[kMsgSize];
+ SSL_CIPHER_description(cipher, msg, kMsgSize);
+ LOG((CLOG_INFO "%s", msg));
+ }
+ return;
+}
+
+void
+SecureSocket::handleTCPConnected(const Event& event, void*)
+{
+ if (getSocket() == nullptr) {
+ LOG((CLOG_DEBUG "disregarding stale connect event"));
+ return;
+ }
+ secureConnect();
+}
diff --git a/src/lib/net/SecureSocket.h b/src/lib/net/SecureSocket.h
new file mode 100644
index 0000000..01d3c3f
--- /dev/null
+++ b/src/lib/net/SecureSocket.h
@@ -0,0 +1,95 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+#include "net/TCPSocket.h"
+#include "net/XSocket.h"
+
+class IEventQueue;
+class SocketMultiplexer;
+class ISocketMultiplexerJob;
+
+struct Ssl;
+
+//! Secure socket
+/*!
+A secure socket using SSL.
+*/
+class SecureSocket : public TCPSocket {
+public:
+ SecureSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family);
+ SecureSocket(IEventQueue* events,
+ SocketMultiplexer* socketMultiplexer,
+ ArchSocket socket);
+ ~SecureSocket();
+
+ // ISocket overrides
+ void close();
+
+ // IDataSocket overrides
+ virtual void connect(const NetworkAddress&);
+
+ ISocketMultiplexerJob*
+ newJob();
+ bool isFatal() const { return m_fatal; }
+ void isFatal(bool b) { m_fatal = b; }
+ bool isSecureReady();
+ void secureConnect();
+ void secureAccept();
+ int secureRead(void* buffer, int size, int& read);
+ int secureWrite(const void* buffer, int size, int& wrote);
+ EJobResult doRead();
+ EJobResult doWrite();
+ void initSsl(bool server);
+ bool loadCertificates(String& CertFile);
+
+private:
+ // SSL
+ void initContext(bool server);
+ void createSSL();
+ int secureAccept(int s);
+ int secureConnect(int s);
+ bool showCertificate();
+ void checkResult(int n, int& retry);
+ void showError(const char* reason = NULL);
+ String getError();
+ void disconnect();
+ void formatFingerprint(String& fingerprint,
+ bool hex = true,
+ bool separator = true);
+ bool verifyCertFingerprint();
+
+ ISocketMultiplexerJob*
+ serviceConnect(ISocketMultiplexerJob*,
+ bool, bool, bool);
+
+ ISocketMultiplexerJob*
+ serviceAccept(ISocketMultiplexerJob*,
+ bool, bool, bool);
+
+ void showSecureConnectInfo();
+ void showSecureLibInfo();
+ void showSecureCipherInfo();
+
+ void handleTCPConnected(const Event& event, void*);
+
+private:
+ Ssl* m_ssl;
+ bool m_secureReady;
+ bool m_fatal;
+};
diff --git a/src/lib/net/SocketMultiplexer.cpp b/src/lib/net/SocketMultiplexer.cpp
new file mode 100644
index 0000000..c4bc64a
--- /dev/null
+++ b/src/lib/net/SocketMultiplexer.cpp
@@ -0,0 +1,352 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "net/SocketMultiplexer.h"
+
+#include "net/ISocketMultiplexerJob.h"
+#include "mt/CondVar.h"
+#include "mt/Lock.h"
+#include "mt/Mutex.h"
+#include "mt/Thread.h"
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+#include "base/Log.h"
+#include "base/TMethodJob.h"
+#include "common/stdvector.h"
+
+//
+// SocketMultiplexer
+//
+
+SocketMultiplexer::SocketMultiplexer() :
+ m_mutex(new Mutex),
+ m_thread(NULL),
+ m_update(false),
+ m_jobsReady(new CondVar<bool>(m_mutex, false)),
+ m_jobListLock(new CondVar<bool>(m_mutex, false)),
+ m_jobListLockLocked(new CondVar<bool>(m_mutex, false)),
+ m_jobListLocker(NULL),
+ m_jobListLockLocker(NULL)
+{
+ // this pointer just has to be unique and not NULL. it will
+ // never be dereferenced. it's used to identify cursor nodes
+ // in the jobs list.
+ // TODO: Remove this evilness
+ m_cursorMark = reinterpret_cast<ISocketMultiplexerJob*>(this);
+
+ // start thread
+ m_thread = new Thread(new TMethodJob<SocketMultiplexer>(
+ this, &SocketMultiplexer::serviceThread));
+}
+
+SocketMultiplexer::~SocketMultiplexer()
+{
+ m_thread->cancel();
+ m_thread->unblockPollSocket();
+ m_thread->wait();
+ delete m_thread;
+ delete m_jobsReady;
+ delete m_jobListLock;
+ delete m_jobListLockLocked;
+ delete m_jobListLocker;
+ delete m_jobListLockLocker;
+ delete m_mutex;
+
+ // clean up jobs
+ for (SocketJobMap::iterator i = m_socketJobMap.begin();
+ i != m_socketJobMap.end(); ++i) {
+ delete *(i->second);
+ }
+}
+
+void
+SocketMultiplexer::addSocket(ISocket* socket, ISocketMultiplexerJob* job)
+{
+ assert(socket != NULL);
+ assert(job != NULL);
+
+ // prevent other threads from locking the job list
+ lockJobListLock();
+
+ // break thread out of poll
+ m_thread->unblockPollSocket();
+
+ // lock the job list
+ lockJobList();
+
+ // insert/replace job
+ SocketJobMap::iterator i = m_socketJobMap.find(socket);
+ if (i == m_socketJobMap.end()) {
+ // we *must* put the job at the end so the order of jobs in
+ // the list continue to match the order of jobs in pfds in
+ // serviceThread().
+ JobCursor j = m_socketJobs.insert(m_socketJobs.end(), job);
+ m_update = true;
+ m_socketJobMap.insert(std::make_pair(socket, j));
+ }
+ else {
+ JobCursor j = i->second;
+ if (*j != job) {
+ delete *j;
+ *j = job;
+ }
+ m_update = true;
+ }
+
+ // unlock the job list
+ unlockJobList();
+}
+
+void
+SocketMultiplexer::removeSocket(ISocket* socket)
+{
+ assert(socket != NULL);
+
+ // prevent other threads from locking the job list
+ lockJobListLock();
+
+ // break thread out of poll
+ m_thread->unblockPollSocket();
+
+ // lock the job list
+ lockJobList();
+
+ // remove job. rather than removing it from the map we put NULL
+ // in the list instead so the order of jobs in the list continues
+ // to match the order of jobs in pfds in serviceThread().
+ SocketJobMap::iterator i = m_socketJobMap.find(socket);
+ if (i != m_socketJobMap.end()) {
+ if (*(i->second) != NULL) {
+ delete *(i->second);
+ *(i->second) = NULL;
+ m_update = true;
+ }
+ }
+
+ // unlock the job list
+ unlockJobList();
+}
+
+void
+SocketMultiplexer::serviceThread(void*)
+{
+ std::vector<IArchNetwork::PollEntry> pfds;
+ IArchNetwork::PollEntry pfd;
+
+ // service the connections
+ for (;;) {
+ Thread::testCancel();
+
+ // wait until there are jobs to handle
+ {
+ Lock lock(m_mutex);
+ while (!(bool)*m_jobsReady) {
+ m_jobsReady->wait();
+ }
+ }
+
+ // lock the job list
+ lockJobListLock();
+ lockJobList();
+
+ // collect poll entries
+ if (m_update) {
+ m_update = false;
+ pfds.clear();
+ pfds.reserve(m_socketJobMap.size());
+
+ JobCursor cursor = newCursor();
+ JobCursor jobCursor = nextCursor(cursor);
+ while (jobCursor != m_socketJobs.end()) {
+ ISocketMultiplexerJob* job = *jobCursor;
+ if (job != NULL) {
+ pfd.m_socket = job->getSocket();
+ pfd.m_events = 0;
+ if (job->isReadable()) {
+ pfd.m_events |= IArchNetwork::kPOLLIN;
+ }
+ if (job->isWritable()) {
+ pfd.m_events |= IArchNetwork::kPOLLOUT;
+ }
+ pfds.push_back(pfd);
+ }
+ jobCursor = nextCursor(cursor);
+ }
+ deleteCursor(cursor);
+ }
+
+ int status;
+ try {
+ // check for status
+ if (!pfds.empty()) {
+ status = ARCH->pollSocket(&pfds[0], (int)pfds.size(), -1);
+ }
+ else {
+ status = 0;
+ }
+ }
+ catch (XArchNetwork& e) {
+ LOG((CLOG_WARN "error in socket multiplexer: %s", e.what()));
+ status = 0;
+ }
+
+ if (status != 0) {
+ // iterate over socket jobs, invoking each and saving the
+ // new job.
+ UInt32 i = 0;
+ JobCursor cursor = newCursor();
+ JobCursor jobCursor = nextCursor(cursor);
+ while (i < pfds.size() && jobCursor != m_socketJobs.end()) {
+ if (*jobCursor != NULL) {
+ // get poll state
+ unsigned short revents = pfds[i].m_revents;
+ bool read = ((revents & IArchNetwork::kPOLLIN) != 0);
+ bool write = ((revents & IArchNetwork::kPOLLOUT) != 0);
+ bool error = ((revents & (IArchNetwork::kPOLLERR |
+ IArchNetwork::kPOLLNVAL)) != 0);
+
+ // run job
+ ISocketMultiplexerJob* job = *jobCursor;
+ ISocketMultiplexerJob* newJob = job->run(read, write, error);
+
+ // save job, if different
+ if (newJob != job) {
+ Lock lock(m_mutex);
+ delete job;
+ *jobCursor = newJob;
+ m_update = true;
+ }
+ ++i;
+ }
+
+ // next job
+ jobCursor = nextCursor(cursor);
+ }
+ deleteCursor(cursor);
+ }
+
+ // delete any removed socket jobs
+ for (SocketJobMap::iterator i = m_socketJobMap.begin();
+ i != m_socketJobMap.end();) {
+ if (*(i->second) == NULL) {
+ m_socketJobs.erase(i->second);
+ m_socketJobMap.erase(i++);
+ m_update = true;
+ }
+ else {
+ ++i;
+ }
+ }
+
+ // unlock the job list
+ unlockJobList();
+ }
+}
+
+SocketMultiplexer::JobCursor
+SocketMultiplexer::newCursor()
+{
+ Lock lock(m_mutex);
+ return m_socketJobs.insert(m_socketJobs.begin(), m_cursorMark);
+}
+
+SocketMultiplexer::JobCursor
+SocketMultiplexer::nextCursor(JobCursor cursor)
+{
+ Lock lock(m_mutex);
+ JobCursor j = m_socketJobs.end();
+ JobCursor i = cursor;
+ while (++i != m_socketJobs.end()) {
+ if (*i != m_cursorMark) {
+ // found a real job (as opposed to a cursor)
+ j = i;
+
+ // move our cursor just past the job
+ m_socketJobs.splice(++i, m_socketJobs, cursor);
+ break;
+ }
+ }
+ return j;
+}
+
+void
+SocketMultiplexer::deleteCursor(JobCursor cursor)
+{
+ Lock lock(m_mutex);
+ m_socketJobs.erase(cursor);
+}
+
+void
+SocketMultiplexer::lockJobListLock()
+{
+ Lock lock(m_mutex);
+
+ // wait for the lock on the lock
+ while (*m_jobListLockLocked) {
+ m_jobListLockLocked->wait();
+ }
+
+ // take ownership of the lock on the lock
+ *m_jobListLockLocked = true;
+ m_jobListLockLocker = new Thread(Thread::getCurrentThread());
+}
+
+void
+SocketMultiplexer::lockJobList()
+{
+ Lock lock(m_mutex);
+
+ // make sure we're the one that called lockJobListLock()
+ assert(*m_jobListLockLocker == Thread::getCurrentThread());
+
+ // wait for the job list lock
+ while (*m_jobListLock) {
+ m_jobListLock->wait();
+ }
+
+ // take ownership of the lock
+ *m_jobListLock = true;
+ m_jobListLocker = m_jobListLockLocker;
+ m_jobListLockLocker = NULL;
+
+ // release the lock on the lock
+ *m_jobListLockLocked = false;
+ m_jobListLockLocked->broadcast();
+}
+
+void
+SocketMultiplexer::unlockJobList()
+{
+ Lock lock(m_mutex);
+
+ // make sure we're the one that called lockJobList()
+ assert(*m_jobListLocker == Thread::getCurrentThread());
+
+ // release the lock
+ delete m_jobListLocker;
+ m_jobListLocker = NULL;
+ *m_jobListLock = false;
+ m_jobListLock->signal();
+
+ // set new jobs ready state
+ bool isReady = !m_socketJobMap.empty();
+ if (*m_jobsReady != isReady) {
+ *m_jobsReady = isReady;
+ m_jobsReady->signal();
+ }
+}
diff --git a/src/lib/net/SocketMultiplexer.h b/src/lib/net/SocketMultiplexer.h
new file mode 100644
index 0000000..4aa39fc
--- /dev/null
+++ b/src/lib/net/SocketMultiplexer.h
@@ -0,0 +1,111 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "arch/IArchNetwork.h"
+#include "common/stdlist.h"
+#include "common/stdmap.h"
+
+template <class T>
+class CondVar;
+class Mutex;
+class Thread;
+class ISocket;
+class ISocketMultiplexerJob;
+
+//! Socket multiplexer
+/*!
+A socket multiplexer services multiple sockets simultaneously.
+*/
+class SocketMultiplexer {
+public:
+ SocketMultiplexer();
+ ~SocketMultiplexer();
+
+ //! @name manipulators
+ //@{
+
+ void addSocket(ISocket*, ISocketMultiplexerJob*);
+
+ void removeSocket(ISocket*);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ // maybe belongs on ISocketMultiplexer
+ static SocketMultiplexer*
+ getInstance();
+
+ //@}
+
+private:
+ // list of jobs. we use a list so we can safely iterate over it
+ // while other threads modify it.
+ typedef std::list<ISocketMultiplexerJob*> SocketJobs;
+ typedef SocketJobs::iterator JobCursor;
+ typedef std::map<ISocket*, JobCursor> SocketJobMap;
+
+ // service sockets. the service thread will only access m_sockets
+ // and m_update while m_pollable and m_polling are true. all other
+ // threads must only modify these when m_pollable and m_polling are
+ // false. only the service thread sets m_polling.
+ void serviceThread(void*);
+
+ // create, iterate, and destroy a cursor. a cursor is used to
+ // safely iterate through the job list while other threads modify
+ // the list. it works by inserting a dummy item in the list and
+ // moving that item through the list. the dummy item will never
+ // be removed by other edits so an iterator pointing at the item
+ // remains valid until we remove the dummy item in deleteCursor().
+ // nextCursor() finds the next non-dummy item, moves our dummy
+ // item just past it, and returns an iterator for the non-dummy
+ // item. all cursor calls lock the mutex for their duration.
+ JobCursor newCursor();
+ JobCursor nextCursor(JobCursor);
+ void deleteCursor(JobCursor);
+
+ // lock out locking the job list. this blocks if another thread
+ // has already locked out locking. once it returns, only the
+ // calling thread will be able to lock the job list after any
+ // current lock is released.
+ void lockJobListLock();
+
+ // lock the job list. this blocks if the job list is already
+ // locked. the calling thread must have called requestJobLock.
+ void lockJobList();
+
+ // unlock the job list and the lock out on locking.
+ void unlockJobList();
+
+private:
+ Mutex* m_mutex;
+ Thread* m_thread;
+ bool m_update;
+ CondVar<bool>* m_jobsReady;
+ CondVar<bool>* m_jobListLock;
+ CondVar<bool>* m_jobListLockLocked;
+ Thread* m_jobListLocker;
+ Thread* m_jobListLockLocker;
+
+ SocketJobs m_socketJobs;
+ SocketJobMap m_socketJobMap;
+ ISocketMultiplexerJob*
+ m_cursorMark;
+};
diff --git a/src/lib/net/TCPListenSocket.cpp b/src/lib/net/TCPListenSocket.cpp
new file mode 100644
index 0000000..8e1540e
--- /dev/null
+++ b/src/lib/net/TCPListenSocket.cpp
@@ -0,0 +1,158 @@
+/*
+ * barrier -- 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 "net/TCPListenSocket.h"
+
+#include "net/NetworkAddress.h"
+#include "net/SocketMultiplexer.h"
+#include "net/TCPSocket.h"
+#include "net/TSocketMultiplexerMethodJob.h"
+#include "net/XSocket.h"
+#include "io/XIO.h"
+#include "mt/Lock.h"
+#include "mt/Mutex.h"
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+#include "base/IEventQueue.h"
+
+//
+// TCPListenSocket
+//
+
+TCPListenSocket::TCPListenSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family) :
+ m_events(events),
+ m_socketMultiplexer(socketMultiplexer)
+{
+ m_mutex = new Mutex;
+ try {
+ m_socket = ARCH->newSocket(family, IArchNetwork::kSTREAM);
+ }
+ catch (XArchNetwork& e) {
+ throw XSocketCreate(e.what());
+ }
+}
+
+TCPListenSocket::~TCPListenSocket()
+{
+ try {
+ if (m_socket != NULL) {
+ m_socketMultiplexer->removeSocket(this);
+ ARCH->closeSocket(m_socket);
+ }
+ }
+ catch (...) {
+ // ignore
+ }
+ delete m_mutex;
+}
+
+void
+TCPListenSocket::bind(const NetworkAddress& addr)
+{
+ try {
+ Lock lock(m_mutex);
+ ARCH->setReuseAddrOnSocket(m_socket, true);
+ ARCH->bindSocket(m_socket, addr.getAddress());
+ ARCH->listenOnSocket(m_socket);
+ m_socketMultiplexer->addSocket(this,
+ new TSocketMultiplexerMethodJob<TCPListenSocket>(
+ this, &TCPListenSocket::serviceListening,
+ m_socket, true, false));
+ }
+ catch (XArchNetworkAddressInUse& e) {
+ throw XSocketAddressInUse(e.what());
+ }
+ catch (XArchNetwork& e) {
+ throw XSocketBind(e.what());
+ }
+}
+
+void
+TCPListenSocket::close()
+{
+ Lock lock(m_mutex);
+ if (m_socket == NULL) {
+ throw XIOClosed();
+ }
+ try {
+ m_socketMultiplexer->removeSocket(this);
+ ARCH->closeSocket(m_socket);
+ m_socket = NULL;
+ }
+ catch (XArchNetwork& e) {
+ throw XSocketIOClose(e.what());
+ }
+}
+
+void*
+TCPListenSocket::getEventTarget() const
+{
+ return const_cast<void*>(static_cast<const void*>(this));
+}
+
+IDataSocket*
+TCPListenSocket::accept()
+{
+ IDataSocket* socket = NULL;
+ try {
+ socket = new TCPSocket(m_events, m_socketMultiplexer, ARCH->acceptSocket(m_socket, NULL));
+ if (socket != NULL) {
+ setListeningJob();
+ }
+ return socket;
+ }
+ catch (XArchNetwork&) {
+ if (socket != NULL) {
+ delete socket;
+ setListeningJob();
+ }
+ return NULL;
+ }
+ catch (std::exception &ex) {
+ if (socket != NULL) {
+ delete socket;
+ setListeningJob();
+ }
+ throw ex;
+ }
+}
+
+void
+TCPListenSocket::setListeningJob()
+{
+ m_socketMultiplexer->addSocket(this,
+ new TSocketMultiplexerMethodJob<TCPListenSocket>(
+ this, &TCPListenSocket::serviceListening,
+ m_socket, true, false));
+}
+
+ISocketMultiplexerJob*
+TCPListenSocket::serviceListening(ISocketMultiplexerJob* job,
+ bool read, bool, bool error)
+{
+ if (error) {
+ close();
+ return NULL;
+ }
+ if (read) {
+ m_events->addEvent(Event(m_events->forIListenSocket().connecting(), this, NULL));
+ // stop polling on this socket until the client accepts
+ return NULL;
+ }
+ return job;
+}
diff --git a/src/lib/net/TCPListenSocket.h b/src/lib/net/TCPListenSocket.h
new file mode 100644
index 0000000..1060356
--- /dev/null
+++ b/src/lib/net/TCPListenSocket.h
@@ -0,0 +1,60 @@
+/*
+ * barrier -- 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 "net/IListenSocket.h"
+#include "arch/IArchNetwork.h"
+
+class Mutex;
+class ISocketMultiplexerJob;
+class IEventQueue;
+class SocketMultiplexer;
+
+//! TCP listen socket
+/*!
+A listen socket using TCP.
+*/
+class TCPListenSocket : public IListenSocket {
+public:
+ TCPListenSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family);
+ virtual ~TCPListenSocket();
+
+ // ISocket overrides
+ virtual void bind(const NetworkAddress&);
+ virtual void close();
+ virtual void* getEventTarget() const;
+
+ // IListenSocket overrides
+ virtual IDataSocket*
+ accept();
+
+protected:
+ void setListeningJob();
+
+public:
+ ISocketMultiplexerJob*
+ serviceListening(ISocketMultiplexerJob*,
+ bool, bool, bool);
+
+protected:
+ ArchSocket m_socket;
+ Mutex* m_mutex;
+ IEventQueue* m_events;
+ SocketMultiplexer* m_socketMultiplexer;
+};
diff --git a/src/lib/net/TCPSocket.cpp b/src/lib/net/TCPSocket.cpp
new file mode 100644
index 0000000..dce81ee
--- /dev/null
+++ b/src/lib/net/TCPSocket.cpp
@@ -0,0 +1,596 @@
+/*
+ * barrier -- 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 "net/TCPSocket.h"
+
+#include "net/NetworkAddress.h"
+#include "net/SocketMultiplexer.h"
+#include "net/TSocketMultiplexerMethodJob.h"
+#include "net/XSocket.h"
+#include "mt/Lock.h"
+#include "arch/Arch.h"
+#include "arch/XArch.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "base/IEventJob.h"
+
+#include <cstring>
+#include <cstdlib>
+#include <memory>
+
+//
+// TCPSocket
+//
+
+TCPSocket::TCPSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family) :
+ IDataSocket(events),
+ m_events(events),
+ m_mutex(),
+ m_flushed(&m_mutex, true),
+ m_socketMultiplexer(socketMultiplexer)
+{
+ try {
+ m_socket = ARCH->newSocket(family, IArchNetwork::kSTREAM);
+ }
+ catch (XArchNetwork& e) {
+ throw XSocketCreate(e.what());
+ }
+
+ LOG((CLOG_DEBUG "Opening new socket: %08X", m_socket));
+
+ init();
+}
+
+TCPSocket::TCPSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, ArchSocket socket) :
+ IDataSocket(events),
+ m_events(events),
+ m_mutex(),
+ m_socket(socket),
+ m_flushed(&m_mutex, true),
+ m_socketMultiplexer(socketMultiplexer)
+{
+ assert(m_socket != NULL);
+
+ LOG((CLOG_DEBUG "Opening new socket: %08X", m_socket));
+
+ // socket starts in connected state
+ init();
+ onConnected();
+ setJob(newJob());
+}
+
+TCPSocket::~TCPSocket()
+{
+ try {
+ close();
+ }
+ catch (...) {
+ // ignore
+ }
+}
+
+void
+TCPSocket::bind(const NetworkAddress& addr)
+{
+ try {
+ ARCH->bindSocket(m_socket, addr.getAddress());
+ }
+ catch (XArchNetworkAddressInUse& e) {
+ throw XSocketAddressInUse(e.what());
+ }
+ catch (XArchNetwork& e) {
+ throw XSocketBind(e.what());
+ }
+}
+
+void
+TCPSocket::close()
+{
+ LOG((CLOG_DEBUG "Closing socket: %08X", m_socket));
+
+ // remove ourself from the multiplexer
+ setJob(NULL);
+
+ Lock lock(&m_mutex);
+
+ // clear buffers and enter disconnected state
+ if (m_connected) {
+ sendEvent(m_events->forISocket().disconnected());
+ }
+ onDisconnected();
+
+ // close the socket
+ if (m_socket != NULL) {
+ ArchSocket socket = m_socket;
+ m_socket = NULL;
+ try {
+ ARCH->closeSocket(socket);
+ }
+ catch (XArchNetwork& e) {
+ // ignore, there's not much we can do
+ LOG((CLOG_WARN "error closing socket: %s", e.what()));
+ }
+ }
+}
+
+void*
+TCPSocket::getEventTarget() const
+{
+ return const_cast<void*>(static_cast<const void*>(this));
+}
+
+UInt32
+TCPSocket::read(void* buffer, UInt32 n)
+{
+ // copy data directly from our input buffer
+ Lock lock(&m_mutex);
+ UInt32 size = m_inputBuffer.getSize();
+ if (n > size) {
+ n = size;
+ }
+ if (buffer != NULL && n != 0) {
+ memcpy(buffer, m_inputBuffer.peek(n), n);
+ }
+ m_inputBuffer.pop(n);
+
+ // if no more data and we cannot read or write then send disconnected
+ if (n > 0 && m_inputBuffer.getSize() == 0 && !m_readable && !m_writable) {
+ sendEvent(m_events->forISocket().disconnected());
+ m_connected = false;
+ }
+
+ return n;
+}
+
+void
+TCPSocket::write(const void* buffer, UInt32 n)
+{
+ bool wasEmpty;
+ {
+ Lock lock(&m_mutex);
+
+ // must not have shutdown output
+ if (!m_writable) {
+ sendEvent(m_events->forIStream().outputError());
+ return;
+ }
+
+ // ignore empty writes
+ if (n == 0) {
+ return;
+ }
+
+ // copy data to the output buffer
+ wasEmpty = (m_outputBuffer.getSize() == 0);
+ m_outputBuffer.write(buffer, n);
+
+ // there's data to write
+ m_flushed = false;
+ }
+
+ // make sure we're waiting to write
+ if (wasEmpty) {
+ setJob(newJob());
+ }
+}
+
+void
+TCPSocket::flush()
+{
+ Lock lock(&m_mutex);
+ while (m_flushed == false) {
+ m_flushed.wait();
+ }
+}
+
+void
+TCPSocket::shutdownInput()
+{
+ bool useNewJob = false;
+ {
+ Lock lock(&m_mutex);
+
+ // shutdown socket for reading
+ try {
+ ARCH->closeSocketForRead(m_socket);
+ }
+ catch (XArchNetwork&) {
+ // ignore
+ }
+
+ // shutdown buffer for reading
+ if (m_readable) {
+ sendEvent(m_events->forIStream().inputShutdown());
+ onInputShutdown();
+ useNewJob = true;
+ }
+ }
+ if (useNewJob) {
+ setJob(newJob());
+ }
+}
+
+void
+TCPSocket::shutdownOutput()
+{
+ bool useNewJob = false;
+ {
+ Lock lock(&m_mutex);
+
+ // shutdown socket for writing
+ try {
+ ARCH->closeSocketForWrite(m_socket);
+ }
+ catch (XArchNetwork&) {
+ // ignore
+ }
+
+ // shutdown buffer for writing
+ if (m_writable) {
+ sendEvent(m_events->forIStream().outputShutdown());
+ onOutputShutdown();
+ useNewJob = true;
+ }
+ }
+ if (useNewJob) {
+ setJob(newJob());
+ }
+}
+
+bool
+TCPSocket::isReady() const
+{
+ Lock lock(&m_mutex);
+ return (m_inputBuffer.getSize() > 0);
+}
+
+bool
+TCPSocket::isFatal() const
+{
+ // TCP sockets aren't ever left in a fatal state.
+ LOG((CLOG_ERR "isFatal() not valid for non-secure connections"));
+ return false;
+}
+
+UInt32
+TCPSocket::getSize() const
+{
+ Lock lock(&m_mutex);
+ return m_inputBuffer.getSize();
+}
+
+void
+TCPSocket::connect(const NetworkAddress& addr)
+{
+ {
+ Lock lock(&m_mutex);
+
+ // fail on attempts to reconnect
+ if (m_socket == NULL || m_connected) {
+ sendConnectionFailedEvent("busy");
+ return;
+ }
+
+ try {
+ if (ARCH->connectSocket(m_socket, addr.getAddress())) {
+ sendEvent(m_events->forIDataSocket().connected());
+ onConnected();
+ }
+ else {
+ // connection is in progress
+ m_writable = true;
+ }
+ }
+ catch (XArchNetwork& e) {
+ throw XSocketConnect(e.what());
+ }
+ }
+ setJob(newJob());
+}
+
+void
+TCPSocket::init()
+{
+ // default state
+ m_connected = false;
+ m_readable = false;
+ m_writable = false;
+
+ try {
+ // turn off Nagle algorithm. we send lots of very short messages
+ // that should be sent without (much) delay. for example, the
+ // mouse motion messages are much less useful if they're delayed.
+ ARCH->setNoDelayOnSocket(m_socket, true);
+ }
+ catch (XArchNetwork& e) {
+ try {
+ ARCH->closeSocket(m_socket);
+ m_socket = NULL;
+ }
+ catch (XArchNetwork&) {
+ // ignore
+ }
+ throw XSocketCreate(e.what());
+ }
+}
+
+TCPSocket::EJobResult
+TCPSocket::doRead()
+{
+ UInt8 buffer[4096];
+ memset(buffer, 0, sizeof(buffer));
+ size_t bytesRead = 0;
+
+ bytesRead = ARCH->readSocket(m_socket, buffer, sizeof(buffer));
+
+ if (bytesRead > 0) {
+ bool wasEmpty = (m_inputBuffer.getSize() == 0);
+
+ // slurp up as much as possible
+ do {
+ m_inputBuffer.write(buffer, (UInt32)bytesRead);
+
+ bytesRead = ARCH->readSocket(m_socket, buffer, sizeof(buffer));
+ } while (bytesRead > 0);
+
+ // send input ready if input buffer was empty
+ if (wasEmpty) {
+ sendEvent(m_events->forIStream().inputReady());
+ }
+ }
+ else {
+ // remote write end of stream hungup. our input side
+ // has therefore shutdown but don't flush our buffer
+ // since there's still data to be read.
+ sendEvent(m_events->forIStream().inputShutdown());
+ if (!m_writable && m_inputBuffer.getSize() == 0) {
+ sendEvent(m_events->forISocket().disconnected());
+ m_connected = false;
+ }
+ m_readable = false;
+ return kNew;
+ }
+
+ return kRetry;
+}
+
+TCPSocket::EJobResult
+TCPSocket::doWrite()
+{
+ // write data
+ UInt32 bufferSize = 0;
+ int bytesWrote = 0;
+
+ bufferSize = m_outputBuffer.getSize();
+ const void* buffer = m_outputBuffer.peek(bufferSize);
+ bytesWrote = (UInt32)ARCH->writeSocket(m_socket, buffer, bufferSize);
+
+ if (bytesWrote > 0) {
+ discardWrittenData(bytesWrote);
+ return kNew;
+ }
+
+ return kRetry;
+}
+
+void
+TCPSocket::setJob(ISocketMultiplexerJob* job)
+{
+ // multiplexer will delete the old job
+ if (job == NULL) {
+ m_socketMultiplexer->removeSocket(this);
+ }
+ else {
+ m_socketMultiplexer->addSocket(this, job);
+ }
+}
+
+ISocketMultiplexerJob*
+TCPSocket::newJob()
+{
+ // note -- must have m_mutex locked on entry
+
+ if (m_socket == NULL) {
+ return NULL;
+ }
+ else if (!m_connected) {
+ assert(!m_readable);
+ if (!(m_readable || m_writable)) {
+ return NULL;
+ }
+ return new TSocketMultiplexerMethodJob<TCPSocket>(
+ this, &TCPSocket::serviceConnecting,
+ m_socket, m_readable, m_writable);
+ }
+ else {
+ if (!(m_readable || (m_writable && (m_outputBuffer.getSize() > 0)))) {
+ return NULL;
+ }
+ return new TSocketMultiplexerMethodJob<TCPSocket>(
+ this, &TCPSocket::serviceConnected,
+ m_socket, m_readable,
+ m_writable && (m_outputBuffer.getSize() > 0));
+ }
+}
+
+void
+TCPSocket::sendConnectionFailedEvent(const char* msg)
+{
+ ConnectionFailedInfo* info = new ConnectionFailedInfo(msg);
+ m_events->addEvent(Event(m_events->forIDataSocket().connectionFailed(),
+ getEventTarget(), info, Event::kDontFreeData));
+}
+
+void
+TCPSocket::sendEvent(Event::Type type)
+{
+ m_events->addEvent(Event(type, getEventTarget(), NULL));
+}
+
+void
+TCPSocket::discardWrittenData(int bytesWrote)
+{
+ m_outputBuffer.pop(bytesWrote);
+ if (m_outputBuffer.getSize() == 0) {
+ sendEvent(m_events->forIStream().outputFlushed());
+ m_flushed = true;
+ m_flushed.broadcast();
+ }
+}
+
+void
+TCPSocket::onConnected()
+{
+ m_connected = true;
+ m_readable = true;
+ m_writable = true;
+}
+
+void
+TCPSocket::onInputShutdown()
+{
+ m_inputBuffer.pop(m_inputBuffer.getSize());
+ m_readable = false;
+}
+
+void
+TCPSocket::onOutputShutdown()
+{
+ m_outputBuffer.pop(m_outputBuffer.getSize());
+ m_writable = false;
+
+ // we're now flushed
+ m_flushed = true;
+ m_flushed.broadcast();
+}
+
+void
+TCPSocket::onDisconnected()
+{
+ // disconnected
+ onInputShutdown();
+ onOutputShutdown();
+ m_connected = false;
+}
+
+ISocketMultiplexerJob*
+TCPSocket::serviceConnecting(ISocketMultiplexerJob* job,
+ bool, bool write, bool error)
+{
+ Lock lock(&m_mutex);
+
+ // should only check for errors if error is true but checking a new
+ // socket (and a socket that's connecting should be new) for errors
+ // should be safe and Mac OS X appears to have a bug where a
+ // non-blocking stream socket that fails to connect immediately is
+ // reported by select as being writable (i.e. connected) even when
+ // the connection has failed. this is easily demonstrated on OS X
+ // 10.3.4 by starting a barrier client and telling to connect to
+ // another system that's not running a barrier server. it will
+ // claim to have connected then quickly disconnect (i guess because
+ // read returns 0 bytes). unfortunately, barrier attempts to
+ // reconnect immediately, the process repeats and we end up
+ // spinning the CPU. luckily, OS X does set SO_ERROR on the
+ // socket correctly when the connection has failed so checking for
+ // errors works. (curiously, sometimes OS X doesn't report
+ // connection refused. when that happens it at least doesn't
+ // report the socket as being writable so barrier is able to time
+ // out the attempt.)
+ if (error || true) {
+ try {
+ // connection may have failed or succeeded
+ ARCH->throwErrorOnSocket(m_socket);
+ }
+ catch (XArchNetwork& e) {
+ sendConnectionFailedEvent(e.what());
+ onDisconnected();
+ return newJob();
+ }
+ }
+
+ if (write) {
+ sendEvent(m_events->forIDataSocket().connected());
+ onConnected();
+ return newJob();
+ }
+
+ return job;
+}
+
+ISocketMultiplexerJob*
+TCPSocket::serviceConnected(ISocketMultiplexerJob* job,
+ bool read, bool write, bool error)
+{
+ Lock lock(&m_mutex);
+
+ if (error) {
+ sendEvent(m_events->forISocket().disconnected());
+ onDisconnected();
+ return newJob();
+ }
+
+ EJobResult result = kRetry;
+ if (write) {
+ try {
+ result = doWrite();
+ }
+ catch (XArchNetworkShutdown&) {
+ // remote read end of stream hungup. our output side
+ // has therefore shutdown.
+ onOutputShutdown();
+ sendEvent(m_events->forIStream().outputShutdown());
+ if (!m_readable && m_inputBuffer.getSize() == 0) {
+ sendEvent(m_events->forISocket().disconnected());
+ m_connected = false;
+ }
+ result = kNew;
+ }
+ catch (XArchNetworkDisconnected&) {
+ // stream hungup
+ onDisconnected();
+ sendEvent(m_events->forISocket().disconnected());
+ result = kNew;
+ }
+ catch (XArchNetwork& e) {
+ // other write error
+ LOG((CLOG_WARN "error writing socket: %s", e.what()));
+ onDisconnected();
+ sendEvent(m_events->forIStream().outputError());
+ sendEvent(m_events->forISocket().disconnected());
+ result = kNew;
+ }
+ }
+
+ if (read && m_readable) {
+ try {
+ result = doRead();
+ }
+ catch (XArchNetworkDisconnected&) {
+ // stream hungup
+ sendEvent(m_events->forISocket().disconnected());
+ onDisconnected();
+ result = kNew;
+ }
+ catch (XArchNetwork& e) {
+ // ignore other read error
+ LOG((CLOG_WARN "error reading socket: %s", e.what()));
+ }
+ }
+
+ return result == kBreak ? NULL : result == kNew ? newJob() : job;
+}
diff --git a/src/lib/net/TCPSocket.h b/src/lib/net/TCPSocket.h
new file mode 100644
index 0000000..1006f88
--- /dev/null
+++ b/src/lib/net/TCPSocket.h
@@ -0,0 +1,116 @@
+/*
+ * barrier -- 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 "net/IDataSocket.h"
+#include "io/StreamBuffer.h"
+#include "mt/CondVar.h"
+#include "mt/Mutex.h"
+#include "arch/IArchNetwork.h"
+
+class Mutex;
+class Thread;
+class ISocketMultiplexerJob;
+class IEventQueue;
+class SocketMultiplexer;
+
+//! TCP data socket
+/*!
+A data socket using TCP.
+*/
+class TCPSocket : public IDataSocket {
+public:
+ TCPSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, IArchNetwork::EAddressFamily family);
+ TCPSocket(IEventQueue* events, SocketMultiplexer* socketMultiplexer, ArchSocket socket);
+ virtual ~TCPSocket();
+
+ // ISocket overrides
+ virtual void bind(const NetworkAddress&);
+ virtual void close();
+ virtual void* getEventTarget() const;
+
+ // IStream overrides
+ virtual UInt32 read(void* buffer, UInt32 n);
+ virtual void write(const void* buffer, UInt32 n);
+ virtual void flush();
+ virtual void shutdownInput();
+ virtual void shutdownOutput();
+ virtual bool isReady() const;
+ virtual bool isFatal() const;
+ virtual UInt32 getSize() const;
+
+ // IDataSocket overrides
+ virtual void connect(const NetworkAddress&);
+
+
+ virtual ISocketMultiplexerJob*
+ newJob();
+
+protected:
+ enum EJobResult {
+ kBreak = -1, //!< Break the Job chain
+ kRetry, //!< Retry the same job
+ kNew //!< Require a new job
+ };
+
+ ArchSocket getSocket() { return m_socket; }
+ IEventQueue* getEvents() { return m_events; }
+ virtual EJobResult doRead();
+ virtual EJobResult doWrite();
+
+ void setJob(ISocketMultiplexerJob*);
+
+ bool isReadable() { return m_readable; }
+ bool isWritable() { return m_writable; }
+
+ Mutex& getMutex() { return m_mutex; }
+
+ void sendEvent(Event::Type);
+ void discardWrittenData(int bytesWrote);
+
+private:
+ void init();
+
+ void sendConnectionFailedEvent(const char*);
+ void onConnected();
+ void onInputShutdown();
+ void onOutputShutdown();
+ void onDisconnected();
+
+ ISocketMultiplexerJob*
+ serviceConnecting(ISocketMultiplexerJob*,
+ bool, bool, bool);
+ ISocketMultiplexerJob*
+ serviceConnected(ISocketMultiplexerJob*,
+ bool, bool, bool);
+
+protected:
+ bool m_readable;
+ bool m_writable;
+ bool m_connected;
+ IEventQueue* m_events;
+ StreamBuffer m_inputBuffer;
+ StreamBuffer m_outputBuffer;
+
+private:
+ Mutex m_mutex;
+ ArchSocket m_socket;
+ CondVar<bool> m_flushed;
+ SocketMultiplexer* m_socketMultiplexer;
+};
diff --git a/src/lib/net/TCPSocketFactory.cpp b/src/lib/net/TCPSocketFactory.cpp
new file mode 100644
index 0000000..6ff4ef8
--- /dev/null
+++ b/src/lib/net/TCPSocketFactory.cpp
@@ -0,0 +1,68 @@
+/*
+ * barrier -- 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 "net/TCPSocketFactory.h"
+#include "net/TCPSocket.h"
+#include "net/TCPListenSocket.h"
+#include "net/SecureSocket.h"
+#include "net/SecureListenSocket.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+
+//
+// TCPSocketFactory
+//
+
+TCPSocketFactory::TCPSocketFactory(IEventQueue* events, SocketMultiplexer* socketMultiplexer) :
+ m_events(events),
+ m_socketMultiplexer(socketMultiplexer)
+{
+ // do nothing
+}
+
+TCPSocketFactory::~TCPSocketFactory()
+{
+ // do nothing
+}
+
+IDataSocket*
+TCPSocketFactory::create(IArchNetwork::EAddressFamily family, bool secure) const
+{
+ if (secure) {
+ SecureSocket* secureSocket = new SecureSocket(m_events, m_socketMultiplexer, family);
+ secureSocket->initSsl (false);
+ return secureSocket;
+ }
+ else {
+ return new TCPSocket(m_events, m_socketMultiplexer, family);
+ }
+}
+
+IListenSocket*
+TCPSocketFactory::createListen(IArchNetwork::EAddressFamily family, bool secure) const
+{
+ IListenSocket* socket = NULL;
+ if (secure) {
+ socket = new SecureListenSocket(m_events, m_socketMultiplexer, family);
+ }
+ else {
+ socket = new TCPListenSocket(m_events, m_socketMultiplexer, family);
+ }
+
+ return socket;
+}
diff --git a/src/lib/net/TCPSocketFactory.h b/src/lib/net/TCPSocketFactory.h
new file mode 100644
index 0000000..0195ec4
--- /dev/null
+++ b/src/lib/net/TCPSocketFactory.h
@@ -0,0 +1,44 @@
+/*
+ * barrier -- 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 "net/ISocketFactory.h"
+#include "arch/IArchNetwork.h"
+
+class IEventQueue;
+class SocketMultiplexer;
+
+//! Socket factory for TCP sockets
+class TCPSocketFactory : public ISocketFactory {
+public:
+ TCPSocketFactory(IEventQueue* events, SocketMultiplexer* socketMultiplexer);
+ virtual ~TCPSocketFactory();
+
+ // ISocketFactory overrides
+ virtual IDataSocket* create(
+ IArchNetwork::EAddressFamily family,
+ bool secure) const;
+ virtual IListenSocket* createListen(
+ IArchNetwork::EAddressFamily family,
+ bool secure) const;
+
+private:
+ IEventQueue* m_events;
+ SocketMultiplexer* m_socketMultiplexer;
+};
diff --git a/src/lib/net/TSocketMultiplexerMethodJob.h b/src/lib/net/TSocketMultiplexerMethodJob.h
new file mode 100644
index 0000000..90efbe7
--- /dev/null
+++ b/src/lib/net/TSocketMultiplexerMethodJob.h
@@ -0,0 +1,109 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "net/ISocketMultiplexerJob.h"
+#include "arch/Arch.h"
+
+//! Use a method as a socket multiplexer job
+/*!
+A socket multiplexer job class that invokes a member function.
+*/
+template <class T>
+class TSocketMultiplexerMethodJob : public ISocketMultiplexerJob {
+public:
+ typedef ISocketMultiplexerJob*
+ (T::*Method)(ISocketMultiplexerJob*, bool, bool, bool);
+
+ //! run() invokes \c object->method(arg)
+ TSocketMultiplexerMethodJob(T* object, Method method,
+ ArchSocket socket, bool readable, bool writeable);
+ virtual ~TSocketMultiplexerMethodJob();
+
+ // IJob overrides
+ virtual ISocketMultiplexerJob*
+ run(bool readable, bool writable, bool error);
+ virtual ArchSocket getSocket() const;
+ virtual bool isReadable() const;
+ virtual bool isWritable() const;
+
+private:
+ T* m_object;
+ Method m_method;
+ ArchSocket m_socket;
+ bool m_readable;
+ bool m_writable;
+ void* m_arg;
+};
+
+template <class T>
+inline
+TSocketMultiplexerMethodJob<T>::TSocketMultiplexerMethodJob(T* object,
+ Method method, ArchSocket socket,
+ bool readable, bool writable) :
+ m_object(object),
+ m_method(method),
+ m_socket(ARCH->copySocket(socket)),
+ m_readable(readable),
+ m_writable(writable)
+{
+ // do nothing
+}
+
+template <class T>
+inline
+TSocketMultiplexerMethodJob<T>::~TSocketMultiplexerMethodJob()
+{
+ ARCH->closeSocket(m_socket);
+}
+
+template <class T>
+inline
+ISocketMultiplexerJob*
+TSocketMultiplexerMethodJob<T>::run(bool read, bool write, bool error)
+{
+ if (m_object != NULL) {
+ return (m_object->*m_method)(this, read, write, error);
+ }
+ return NULL;
+}
+
+template <class T>
+inline
+ArchSocket
+TSocketMultiplexerMethodJob<T>::getSocket() const
+{
+ return m_socket;
+}
+
+template <class T>
+inline
+bool
+TSocketMultiplexerMethodJob<T>::isReadable() const
+{
+ return m_readable;
+}
+
+template <class T>
+inline
+bool
+TSocketMultiplexerMethodJob<T>::isWritable() const
+{
+ return m_writable;
+}
diff --git a/src/lib/net/XSocket.cpp b/src/lib/net/XSocket.cpp
new file mode 100644
index 0000000..13e0fc3
--- /dev/null
+++ b/src/lib/net/XSocket.cpp
@@ -0,0 +1,117 @@
+/*
+ * barrier -- 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 "net/XSocket.h"
+#include "base/String.h"
+
+//
+// XSocketAddress
+//
+
+XSocketAddress::XSocketAddress(EError error,
+ const String& hostname, int port) _NOEXCEPT :
+ m_error(error),
+ m_hostname(hostname),
+ m_port(port)
+{
+ // do nothing
+}
+
+XSocketAddress::EError
+XSocketAddress::getError() const throw()
+{
+ return m_error;
+}
+
+String
+XSocketAddress::getHostname() const throw()
+{
+ return m_hostname;
+}
+
+int
+XSocketAddress::getPort() const throw()
+{
+ return m_port;
+}
+
+String
+XSocketAddress::getWhat() const throw()
+{
+ static const char* s_errorID[] = {
+ "XSocketAddressUnknown",
+ "XSocketAddressNotFound",
+ "XSocketAddressNoAddress",
+ "XSocketAddressUnsupported",
+ "XSocketAddressBadPort"
+ };
+ static const char* s_errorMsg[] = {
+ "unknown error for: %{1}:%{2}",
+ "address not found for: %{1}",
+ "no address for: %{1}",
+ "unsupported address for: %{1}",
+ "invalid port" // m_port may not be set to the bad port
+ };
+ return format(s_errorID[m_error], s_errorMsg[m_error],
+ m_hostname.c_str(),
+ barrier::string::sprintf("%d", m_port).c_str());
+}
+
+
+//
+// XSocketIOClose
+//
+
+String
+XSocketIOClose::getWhat() const throw()
+{
+ return format("XSocketIOClose", "close: %{1}", what());
+}
+
+
+//
+// XSocketBind
+//
+
+String
+XSocketBind::getWhat() const throw()
+{
+ return format("XSocketBind", "cannot bind address: %{1}", what());
+}
+
+
+//
+// XSocketConnect
+//
+
+String
+XSocketConnect::getWhat() const throw()
+{
+ return format("XSocketConnect", "cannot connect socket: %{1}", what());
+}
+
+
+//
+// XSocketCreate
+//
+
+String
+XSocketCreate::getWhat() const throw()
+{
+ return format("XSocketCreate", "cannot create socket: %{1}", what());
+}
diff --git a/src/lib/net/XSocket.h b/src/lib/net/XSocket.h
new file mode 100644
index 0000000..be02b5b
--- /dev/null
+++ b/src/lib/net/XSocket.h
@@ -0,0 +1,98 @@
+/*
+ * barrier -- 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 "io/XIO.h"
+#include "base/XBase.h"
+#include "base/String.h"
+#include "common/basic_types.h"
+
+//! Generic socket exception
+XBASE_SUBCLASS(XSocket, XBase);
+
+//! Socket bad address exception
+/*!
+Thrown when attempting to create an invalid network address.
+*/
+class XSocketAddress : public XSocket {
+public:
+ //! Failure codes
+ enum EError {
+ kUnknown, //!< Unknown error
+ kNotFound, //!< The hostname is unknown
+ kNoAddress, //!< The hostname is valid but has no IP address
+ kUnsupported, //!< The hostname is valid but has no supported address
+ kBadPort //!< The port is invalid
+ };
+
+ XSocketAddress(EError, const String& hostname, int port) _NOEXCEPT;
+ virtual ~XSocketAddress() _NOEXCEPT { }
+
+ //! @name accessors
+ //@{
+
+ //! Get the error code
+ EError getError() const throw();
+ //! Get the hostname
+ String getHostname() const throw();
+ //! Get the port
+ int getPort() const throw();
+
+ //@}
+
+protected:
+ // XBase overrides
+ virtual String getWhat() const throw();
+
+private:
+ EError m_error;
+ String m_hostname;
+ int m_port;
+};
+
+//! I/O closing exception
+/*!
+Thrown if a stream cannot be closed.
+*/
+XBASE_SUBCLASS_FORMAT(XSocketIOClose, XIOClose);
+
+//! Socket cannot bind address exception
+/*!
+Thrown when a socket cannot be bound to an address.
+*/
+XBASE_SUBCLASS_FORMAT(XSocketBind, XSocket);
+
+//! Socket address in use exception
+/*!
+Thrown when a socket cannot be bound to an address because the address
+is already in use.
+*/
+XBASE_SUBCLASS(XSocketAddressInUse, XSocketBind);
+
+//! Cannot connect socket exception
+/*!
+Thrown when a socket cannot connect to a remote endpoint.
+*/
+XBASE_SUBCLASS_FORMAT(XSocketConnect, XSocket);
+
+//! Cannot create socket exception
+/*!
+Thrown when a socket cannot be created (by the operating system).
+*/
+XBASE_SUBCLASS_FORMAT(XSocketCreate, XSocket);
diff --git a/src/lib/platform/CMakeLists.txt b/src/lib/platform/CMakeLists.txt
new file mode 100644
index 0000000..a1718a1
--- /dev/null
+++ b/src/lib/platform/CMakeLists.txt
@@ -0,0 +1,49 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+if (WIN32)
+ file(GLOB headers "MSWindows*.h" "ImmuneKeysReader.h" "synwinhk.h")
+ file(GLOB sources "MSWindows*.cpp" "ImmuneKeysReader.cpp")
+elseif (APPLE)
+ file(GLOB headers "OSX*.h" "IOSX*.h")
+ file(GLOB sources "OSX*.cpp" "IOSX*.cpp" "OSX*.m" "OSX*.mm")
+elseif (UNIX)
+ file(GLOB headers "XWindows*.h")
+ file(GLOB sources "XWindows*.cpp")
+endif()
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+if (APPLE)
+ list(APPEND inc
+ /System/Library/Frameworks
+ )
+endif()
+
+include_directories(${inc})
+add_library(platform STATIC ${sources})
+target_link_libraries(platform client ${libs})
+
+if (UNIX)
+ target_link_libraries(platform io net ipc synlib client ${libs})
+endif()
+
+if (APPLE)
+ find_library(COCOA_LIBRARY Cocoa)
+ target_link_libraries(platform ${COCOA_LIBRARY})
+endif()
diff --git a/src/lib/platform/IMSWindowsClipboardFacade.h b/src/lib/platform/IMSWindowsClipboardFacade.h
new file mode 100644
index 0000000..03c6248
--- /dev/null
+++ b/src/lib/platform/IMSWindowsClipboardFacade.h
@@ -0,0 +1,36 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#ifndef IMWINDOWSCLIPBOARDFACADE
+#define IMWINDOWSCLIPBOARDFACADE
+
+#include "common/IInterface.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+class IMSWindowsClipboardConverter;
+
+class IMSWindowsClipboardFacade : public IInterface
+{
+public:
+ virtual void write(HANDLE win32Data, UINT win32Format) = 0;
+ virtual ~IMSWindowsClipboardFacade() { }
+};
+
+#endif \ No newline at end of file
diff --git a/src/lib/platform/IOSXKeyResource.cpp b/src/lib/platform/IOSXKeyResource.cpp
new file mode 100644
index 0000000..0c5abe7
--- /dev/null
+++ b/src/lib/platform/IOSXKeyResource.cpp
@@ -0,0 +1,189 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 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 "platform/IOSXKeyResource.h"
+
+#include <Carbon/Carbon.h>
+
+KeyID
+IOSXKeyResource::getKeyID(UInt8 c)
+{
+ if (c == 0) {
+ return kKeyNone;
+ }
+ else if (c >= 32 && c < 127) {
+ // ASCII
+ return static_cast<KeyID>(c);
+ }
+ else {
+ // handle special keys
+ switch (c) {
+ case 0x01:
+ return kKeyHome;
+
+ case 0x02:
+ return kKeyKP_Enter;
+
+ case 0x03:
+ return kKeyKP_Enter;
+
+ case 0x04:
+ return kKeyEnd;
+
+ case 0x05:
+ return kKeyHelp;
+
+ case 0x08:
+ return kKeyBackSpace;
+
+ case 0x09:
+ return kKeyTab;
+
+ case 0x0b:
+ return kKeyPageUp;
+
+ case 0x0c:
+ return kKeyPageDown;
+
+ case 0x0d:
+ return kKeyReturn;
+
+ case 0x10:
+ // OS X maps all the function keys (F1, etc) to this one key.
+ // we can't determine the right key here so we have to do it
+ // some other way.
+ return kKeyNone;
+
+ case 0x1b:
+ return kKeyEscape;
+
+ case 0x1c:
+ return kKeyLeft;
+
+ case 0x1d:
+ return kKeyRight;
+
+ case 0x1e:
+ return kKeyUp;
+
+ case 0x1f:
+ return kKeyDown;
+
+ case 0x7f:
+ return kKeyDelete;
+
+ case 0x06:
+ case 0x07:
+ case 0x0a:
+ case 0x0e:
+ case 0x0f:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ case 0x16:
+ case 0x17:
+ case 0x18:
+ case 0x19:
+ case 0x1a:
+ // discard other control characters
+ return kKeyNone;
+
+ default:
+ // not special or unknown
+ break;
+ }
+
+ // create string with character
+ char str[2];
+ str[0] = static_cast<char>(c);
+ str[1] = 0;
+
+ // get current keyboard script
+ TISInputSourceRef isref = TISCopyCurrentKeyboardInputSource();
+ CFArrayRef langs = (CFArrayRef) TISGetInputSourceProperty(isref, kTISPropertyInputSourceLanguages);
+ CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding(
+ (CFStringRef)CFArrayGetValueAtIndex(langs, 0));
+ // convert to unicode
+ CFStringRef cfString =
+ CFStringCreateWithCStringNoCopy(
+ kCFAllocatorDefault, str, encoding, kCFAllocatorNull);
+
+ // sometimes CFStringCreate...() returns NULL (e.g. Apple Korean
+ // encoding with char value 214). if it did then make no key,
+ // otherwise CFStringCreateMutableCopy() will crash.
+ if (cfString == NULL) {
+ return kKeyNone;
+ }
+
+ // convert to precomposed
+ CFMutableStringRef mcfString =
+ CFStringCreateMutableCopy(kCFAllocatorDefault, 0, cfString);
+ CFRelease(cfString);
+ CFStringNormalize(mcfString, kCFStringNormalizationFormC);
+
+ // check result
+ int unicodeLength = CFStringGetLength(mcfString);
+ if (unicodeLength == 0) {
+ CFRelease(mcfString);
+ return kKeyNone;
+ }
+ if (unicodeLength > 1) {
+ // FIXME -- more than one character, we should handle this
+ CFRelease(mcfString);
+ return kKeyNone;
+ }
+
+ // get unicode character
+ UniChar uc = CFStringGetCharacterAtIndex(mcfString, 0);
+ CFRelease(mcfString);
+
+ // convert to KeyID
+ return static_cast<KeyID>(uc);
+ }
+}
+
+KeyID
+IOSXKeyResource::unicharToKeyID(UniChar c)
+{
+ switch (c) {
+ case 3:
+ return kKeyKP_Enter;
+
+ case 8:
+ return kKeyBackSpace;
+
+ case 9:
+ return kKeyTab;
+
+ case 13:
+ return kKeyReturn;
+
+ case 27:
+ return kKeyEscape;
+
+ case 127:
+ return kKeyDelete;
+
+ default:
+ if (c < 32) {
+ return kKeyNone;
+ }
+ return static_cast<KeyID>(c);
+ }
+}
diff --git a/src/lib/platform/IOSXKeyResource.h b/src/lib/platform/IOSXKeyResource.h
new file mode 100644
index 0000000..fc190ef
--- /dev/null
+++ b/src/lib/platform/IOSXKeyResource.h
@@ -0,0 +1,36 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 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/>.
+ */
+
+#pragma once
+
+#include "barrier/KeyState.h"
+
+class IOSXKeyResource : public IInterface {
+public:
+ virtual bool isValid() const = 0;
+ virtual UInt32 getNumModifierCombinations() const = 0;
+ virtual UInt32 getNumTables() const = 0;
+ virtual UInt32 getNumButtons() const = 0;
+ virtual UInt32 getTableForModifier(UInt32 mask) const = 0;
+ virtual KeyID getKey(UInt32 table, UInt32 button) const = 0;
+
+ // Convert a character in the current script to the equivalent KeyID
+ static KeyID getKeyID(UInt8);
+
+ // Convert a unicode character to the equivalent KeyID.
+ static KeyID unicharToKeyID(UniChar);
+};
diff --git a/src/lib/platform/ImmuneKeysReader.cpp b/src/lib/platform/ImmuneKeysReader.cpp
new file mode 100644
index 0000000..72baed3
--- /dev/null
+++ b/src/lib/platform/ImmuneKeysReader.cpp
@@ -0,0 +1,53 @@
+/*
+* barrier -- mouse and keyboard sharing utility
+* Copyright (C) 2018 Deuauche Open Source Group
+*
+* 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 "ImmuneKeysReader.h"
+
+#include <fstream>
+
+const std::size_t AllocatedLineSize = 1024;
+const char CommentChar = '#';
+
+static void add_key(const char * const buffer, std::vector<DWORD> &keys)
+{
+ const char *first;
+ // skip spaces. ignore blank lines and comment lines
+ for (first = buffer; *first == ' '; ++first);
+ if (*first != 0 && *first != CommentChar)
+ keys.emplace_back(std::stoul(first, 0, 0));
+}
+
+/*static*/ bool ImmuneKeysReader::get_list(const char * const path, std::vector<DWORD> &keys, std::string &badline)
+{
+ // default values for return params
+ keys.clear();
+ badline.clear();
+ std::ifstream stream(path, std::ios::in);
+ if (stream.is_open()) {
+ // size includes the null-terminator
+ char buffer[AllocatedLineSize];
+ while (stream.getline(&buffer[0], AllocatedLineSize)) {
+ try {
+ add_key(buffer, keys);
+ } catch (...) {
+ badline = buffer;
+ return false;
+ }
+ }
+ }
+ return true;
+} \ No newline at end of file
diff --git a/src/lib/platform/ImmuneKeysReader.h b/src/lib/platform/ImmuneKeysReader.h
new file mode 100644
index 0000000..b46cbbe
--- /dev/null
+++ b/src/lib/platform/ImmuneKeysReader.h
@@ -0,0 +1,34 @@
+/*
+* barrier -- mouse and keyboard sharing utility
+* Copyright (C) 2018 Deuauche Open Source Group
+*
+* 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 <vector>
+#include <string>
+
+// let's not import all of Windows just to get this typedef
+typedef unsigned long DWORD;
+
+class ImmuneKeysReader
+{
+public:
+ static bool get_list(const char * const path, std::vector<DWORD> &keys, std::string &badLine);
+
+private:
+ // static class
+ explicit ImmuneKeysReader() {}
+};
diff --git a/src/lib/platform/MSWindowsClipboard.cpp b/src/lib/platform/MSWindowsClipboard.cpp
new file mode 100644
index 0000000..8ab50df
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboard.cpp
@@ -0,0 +1,232 @@
+/*
+ * barrier -- 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 "platform/MSWindowsClipboard.h"
+
+#include "platform/MSWindowsClipboardTextConverter.h"
+#include "platform/MSWindowsClipboardUTF16Converter.h"
+#include "platform/MSWindowsClipboardBitmapConverter.h"
+#include "platform/MSWindowsClipboardHTMLConverter.h"
+#include "platform/MSWindowsClipboardFacade.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "base/Log.h"
+
+//
+// MSWindowsClipboard
+//
+
+UINT MSWindowsClipboard::s_ownershipFormat = 0;
+
+MSWindowsClipboard::MSWindowsClipboard(HWND window) :
+ m_window(window),
+ m_time(0),
+ m_facade(new MSWindowsClipboardFacade()),
+ m_deleteFacade(true)
+{
+ // add converters, most desired first
+ m_converters.push_back(new MSWindowsClipboardUTF16Converter);
+ m_converters.push_back(new MSWindowsClipboardBitmapConverter);
+ m_converters.push_back(new MSWindowsClipboardHTMLConverter);
+}
+
+MSWindowsClipboard::~MSWindowsClipboard()
+{
+ clearConverters();
+
+ // dependency injection causes confusion over ownership, so we need
+ // logic to decide whether or not we delete the facade. there must
+ // be a more elegant way of doing this.
+ if (m_deleteFacade)
+ delete m_facade;
+}
+
+void
+MSWindowsClipboard::setFacade(IMSWindowsClipboardFacade& facade)
+{
+ delete m_facade;
+ m_facade = &facade;
+ m_deleteFacade = false;
+}
+
+bool
+MSWindowsClipboard::emptyUnowned()
+{
+ LOG((CLOG_DEBUG "empty clipboard"));
+
+ // empty the clipboard (and take ownership)
+ if (!EmptyClipboard()) {
+ // unable to cause this in integ tests, but this error has never
+ // actually been reported by users.
+ LOG((CLOG_DEBUG "failed to grab clipboard"));
+ return false;
+ }
+
+ return true;
+}
+
+bool
+MSWindowsClipboard::empty()
+{
+ if (!emptyUnowned()) {
+ return false;
+ }
+
+ // mark clipboard as being owned by barrier
+ HGLOBAL data = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, 1);
+ if (NULL == SetClipboardData(getOwnershipFormat(), data)) {
+ LOG((CLOG_DEBUG "failed to set clipboard data"));
+ GlobalFree(data);
+ return false;
+ }
+
+ return true;
+}
+
+void
+MSWindowsClipboard::add(EFormat format, const String& data)
+{
+ LOG((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format));
+
+ // convert data to win32 form
+ for (ConverterList::const_iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ IMSWindowsClipboardConverter* converter = *index;
+
+ // skip converters for other formats
+ if (converter->getFormat() == format) {
+ HANDLE win32Data = converter->fromIClipboard(data);
+ if (win32Data != NULL) {
+ UINT win32Format = converter->getWin32Format();
+ m_facade->write(win32Data, win32Format);
+ }
+ }
+ }
+}
+
+bool
+MSWindowsClipboard::open(Time time) const
+{
+ LOG((CLOG_DEBUG "open clipboard"));
+
+ if (!OpenClipboard(m_window)) {
+ // unable to cause this in integ tests; but this can happen!
+ // * http://symless.com/pm/issues/86
+ // * http://symless.com/pm/issues/1256
+ // logging improved to see if we can catch more info next time.
+ LOG((CLOG_WARN "failed to open clipboard: %d", GetLastError()));
+ return false;
+ }
+
+ m_time = time;
+
+ return true;
+}
+
+void
+MSWindowsClipboard::close() const
+{
+ LOG((CLOG_DEBUG "close clipboard"));
+ CloseClipboard();
+}
+
+IClipboard::Time
+MSWindowsClipboard::getTime() const
+{
+ return m_time;
+}
+
+bool
+MSWindowsClipboard::has(EFormat format) const
+{
+ for (ConverterList::const_iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ IMSWindowsClipboardConverter* converter = *index;
+ if (converter->getFormat() == format) {
+ if (IsClipboardFormatAvailable(converter->getWin32Format())) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+String
+MSWindowsClipboard::get(EFormat format) const
+{
+ // find the converter for the first clipboard format we can handle
+ IMSWindowsClipboardConverter* converter = NULL;
+ for (ConverterList::const_iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+
+ converter = *index;
+ if (converter->getFormat() == format) {
+ break;
+ }
+ converter = NULL;
+ }
+
+ // if no converter then we don't recognize any formats
+ if (converter == NULL) {
+ LOG((CLOG_WARN "no converter for format %d", format));
+ return String();
+ }
+
+ // get a handle to the clipboard data
+ HANDLE win32Data = GetClipboardData(converter->getWin32Format());
+ if (win32Data == NULL) {
+ // nb: can't cause this using integ tests; this is only caused when
+ // the selected converter returns an invalid format -- which you
+ // cannot cause using public functions.
+ return String();
+ }
+
+ // convert
+ return converter->toIClipboard(win32Data);
+}
+
+void
+MSWindowsClipboard::clearConverters()
+{
+ for (ConverterList::iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ delete *index;
+ }
+ m_converters.clear();
+}
+
+bool
+MSWindowsClipboard::isOwnedByBarrier()
+{
+ // create ownership format if we haven't yet
+ if (s_ownershipFormat == 0) {
+ s_ownershipFormat = RegisterClipboardFormat(TEXT("BarrierOwnership"));
+ }
+ return (IsClipboardFormatAvailable(getOwnershipFormat()) != 0);
+}
+
+UINT
+MSWindowsClipboard::getOwnershipFormat()
+{
+ // create ownership format if we haven't yet
+ if (s_ownershipFormat == 0) {
+ s_ownershipFormat = RegisterClipboardFormat(TEXT("BarrierOwnership"));
+ }
+
+ // return the format
+ return s_ownershipFormat;
+}
diff --git a/src/lib/platform/MSWindowsClipboard.h b/src/lib/platform/MSWindowsClipboard.h
new file mode 100644
index 0000000..3e92a39
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboard.h
@@ -0,0 +1,113 @@
+/*
+ * barrier -- 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 "platform/MSWindowsClipboardFacade.h"
+#include "barrier/IClipboard.h"
+#include "common/stdvector.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+class IMSWindowsClipboardConverter;
+class IMSWindowsClipboardFacade;
+
+//! Microsoft windows clipboard implementation
+class MSWindowsClipboard : public IClipboard {
+public:
+ MSWindowsClipboard(HWND window);
+ MSWindowsClipboard(HWND window, IMSWindowsClipboardFacade &facade);
+ virtual ~MSWindowsClipboard();
+
+ //! Empty clipboard without ownership
+ /*!
+ Take ownership of the clipboard and clear all data from it.
+ This must be called between a successful open() and close().
+ Return false if the clipboard ownership could not be taken;
+ the clipboard should not be emptied in this case. Unlike
+ empty(), isOwnedByBarrier() will return false when emptied
+ this way. This is useful when barrier wants to put data on
+ clipboard but pretend (to itself) that some other app did it.
+ When using empty(), barrier assumes the data came from the
+ server and doesn't need to be sent back. emptyUnowned()
+ makes barrier send the data to the server.
+ */
+ bool emptyUnowned();
+
+ //! Test if clipboard is owned by barrier
+ static bool isOwnedByBarrier();
+
+ // IClipboard overrides
+ virtual bool empty();
+ virtual void add(EFormat, const String& data);
+ virtual bool open(Time) const;
+ virtual void close() const;
+ virtual Time getTime() const;
+ virtual bool has(EFormat) const;
+ virtual String get(EFormat) const;
+
+ void setFacade(IMSWindowsClipboardFacade& facade);
+
+private:
+ void clearConverters();
+
+ UINT convertFormatToWin32(EFormat) const;
+ HANDLE convertTextToWin32(const String& data) const;
+ String convertTextFromWin32(HANDLE) const;
+
+ static UINT getOwnershipFormat();
+
+private:
+ typedef std::vector<IMSWindowsClipboardConverter*> ConverterList;
+
+ HWND m_window;
+ mutable Time m_time;
+ ConverterList m_converters;
+ static UINT s_ownershipFormat;
+ IMSWindowsClipboardFacade* m_facade;
+ bool m_deleteFacade;
+};
+
+//! Clipboard format converter interface
+/*!
+This interface defines the methods common to all win32 clipboard format
+converters.
+*/
+class IMSWindowsClipboardConverter : public IInterface {
+public:
+ // accessors
+
+ // return the clipboard format this object converts from/to
+ virtual IClipboard::EFormat
+ getFormat() const = 0;
+
+ // return the atom representing the win32 clipboard format that
+ // this object converts from/to
+ virtual UINT getWin32Format() const = 0;
+
+ // convert from the IClipboard format to the win32 clipboard format.
+ // the input data must be in the IClipboard format returned by
+ // getFormat(). the return data will be in the win32 clipboard
+ // format returned by getWin32Format(), allocated by GlobalAlloc().
+ virtual HANDLE fromIClipboard(const String&) const = 0;
+
+ // convert from the win32 clipboard format to the IClipboard format
+ // (i.e., the reverse of fromIClipboard()).
+ virtual String toIClipboard(HANDLE data) const = 0;
+};
diff --git a/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp b/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp
new file mode 100644
index 0000000..decbad6
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardAnyTextConverter.cpp
@@ -0,0 +1,149 @@
+/*
+ * barrier -- 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 "platform/MSWindowsClipboardAnyTextConverter.h"
+
+//
+// MSWindowsClipboardAnyTextConverter
+//
+
+MSWindowsClipboardAnyTextConverter::MSWindowsClipboardAnyTextConverter()
+{
+ // do nothing
+}
+
+MSWindowsClipboardAnyTextConverter::~MSWindowsClipboardAnyTextConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+MSWindowsClipboardAnyTextConverter::getFormat() const
+{
+ return IClipboard::kText;
+}
+
+HANDLE
+MSWindowsClipboardAnyTextConverter::fromIClipboard(const String& data) const
+{
+ // convert linefeeds and then convert to desired encoding
+ String text = doFromIClipboard(convertLinefeedToWin32(data));
+ UInt32 size = (UInt32)text.size();
+
+ // copy to memory handle
+ HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, size);
+ if (gData != NULL) {
+ // get a pointer to the allocated memory
+ char* dst = (char*)GlobalLock(gData);
+ if (dst != NULL) {
+ memcpy(dst, text.data(), size);
+ GlobalUnlock(gData);
+ }
+ else {
+ GlobalFree(gData);
+ gData = NULL;
+ }
+ }
+
+ return gData;
+}
+
+String
+MSWindowsClipboardAnyTextConverter::toIClipboard(HANDLE data) const
+{
+ // get datator
+ const char* src = (const char*)GlobalLock(data);
+ UInt32 srcSize = (UInt32)GlobalSize(data);
+ if (src == NULL || srcSize <= 1) {
+ return String();
+ }
+
+ // convert text
+ String text = doToIClipboard(String(src, srcSize));
+
+ // release handle
+ GlobalUnlock(data);
+
+ // convert newlines
+ return convertLinefeedToUnix(text);
+}
+
+String
+MSWindowsClipboardAnyTextConverter::convertLinefeedToWin32(
+ const String& src) const
+{
+ // note -- we assume src is a valid UTF-8 string
+
+ // count newlines in string
+ UInt32 numNewlines = 0;
+ UInt32 n = (UInt32)src.size();
+ for (const char* scan = src.c_str(); n > 0; ++scan, --n) {
+ if (*scan == '\n') {
+ ++numNewlines;
+ }
+ }
+ if (numNewlines == 0) {
+ return src;
+ }
+
+ // allocate new string
+ String dst;
+ dst.reserve(src.size() + numNewlines);
+
+ // copy string, converting newlines
+ n = (UInt32)src.size();
+ for (const char* scan = src.c_str(); n > 0; ++scan, --n) {
+ if (scan[0] == '\n') {
+ dst += '\r';
+ }
+ dst += scan[0];
+ }
+
+ return dst;
+}
+
+String
+MSWindowsClipboardAnyTextConverter::convertLinefeedToUnix(
+ const String& src) const
+{
+ // count newlines in string
+ UInt32 numNewlines = 0;
+ UInt32 n = (UInt32)src.size();
+ for (const char* scan = src.c_str(); n > 0; ++scan, --n) {
+ if (scan[0] == '\r' && scan[1] == '\n') {
+ ++numNewlines;
+ }
+ }
+ if (numNewlines == 0) {
+ return src;
+ }
+
+ // allocate new string
+ String dst;
+ dst.reserve(src.size());
+
+ // copy string, converting newlines
+ n = (UInt32)src.size();
+ for (const char* scan = src.c_str(); n > 0; ++scan, --n) {
+ if (scan[0] != '\r' || scan[1] != '\n') {
+ dst += scan[0];
+ }
+ }
+
+ return dst;
+}
diff --git a/src/lib/platform/MSWindowsClipboardAnyTextConverter.h b/src/lib/platform/MSWindowsClipboardAnyTextConverter.h
new file mode 100644
index 0000000..cabdb0b
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardAnyTextConverter.h
@@ -0,0 +1,57 @@
+/*
+ * barrier -- 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 "platform/MSWindowsClipboard.h"
+
+//! Convert to/from some text encoding
+class MSWindowsClipboardAnyTextConverter :
+ public IMSWindowsClipboardConverter {
+public:
+ MSWindowsClipboardAnyTextConverter();
+ virtual ~MSWindowsClipboardAnyTextConverter();
+
+ // IMSWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual UINT getWin32Format() const = 0;
+ virtual HANDLE fromIClipboard(const String&) const;
+ virtual String toIClipboard(HANDLE) const;
+
+protected:
+ //! Convert from IClipboard format
+ /*!
+ Do UTF-8 conversion only. Memory handle allocation and
+ linefeed conversion is done by this class. doFromIClipboard()
+ must include the nul terminator in the returned string (not
+ including the String's nul terminator).
+ */
+ virtual String doFromIClipboard(const String&) const = 0;
+
+ //! Convert to IClipboard format
+ /*!
+ Do UTF-8 conversion only. Memory handle allocation and
+ linefeed conversion is done by this class.
+ */
+ virtual String doToIClipboard(const String&) const = 0;
+
+private:
+ String convertLinefeedToWin32(const String&) const;
+ String convertLinefeedToUnix(const String&) const;
+};
diff --git a/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp b/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp
new file mode 100644
index 0000000..16bd4bf
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardBitmapConverter.cpp
@@ -0,0 +1,152 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/MSWindowsClipboardBitmapConverter.h"
+
+#include "base/Log.h"
+
+//
+// MSWindowsClipboardBitmapConverter
+//
+
+MSWindowsClipboardBitmapConverter::MSWindowsClipboardBitmapConverter()
+{
+ // do nothing
+}
+
+MSWindowsClipboardBitmapConverter::~MSWindowsClipboardBitmapConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+MSWindowsClipboardBitmapConverter::getFormat() const
+{
+ return IClipboard::kBitmap;
+}
+
+UINT
+MSWindowsClipboardBitmapConverter::getWin32Format() const
+{
+ return CF_DIB;
+}
+
+HANDLE
+MSWindowsClipboardBitmapConverter::fromIClipboard(const String& data) const
+{
+ // copy to memory handle
+ HGLOBAL gData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, data.size());
+ if (gData != NULL) {
+ // get a pointer to the allocated memory
+ char* dst = (char*)GlobalLock(gData);
+ if (dst != NULL) {
+ memcpy(dst, data.data(), data.size());
+ GlobalUnlock(gData);
+ }
+ else {
+ GlobalFree(gData);
+ gData = NULL;
+ }
+ }
+
+ return gData;
+}
+
+String
+MSWindowsClipboardBitmapConverter::toIClipboard(HANDLE data) const
+{
+ // get datator
+ LPVOID src = GlobalLock(data);
+ if (src == NULL) {
+ return String();
+ }
+ UInt32 srcSize = (UInt32)GlobalSize(data);
+
+ // check image type
+ const BITMAPINFO* bitmap = static_cast<const BITMAPINFO*>(src);
+ LOG((CLOG_INFO "bitmap: %dx%d %d", bitmap->bmiHeader.biWidth, bitmap->bmiHeader.biHeight, (int)bitmap->bmiHeader.biBitCount));
+ if (bitmap->bmiHeader.biPlanes == 1 &&
+ (bitmap->bmiHeader.biBitCount == 24 ||
+ bitmap->bmiHeader.biBitCount == 32) &&
+ bitmap->bmiHeader.biCompression == BI_RGB) {
+ // already in canonical form
+ String image(static_cast<char const*>(src), srcSize);
+ GlobalUnlock(data);
+ return image;
+ }
+
+ // create a destination DIB section
+ LOG((CLOG_INFO "convert image from: depth=%d comp=%d", bitmap->bmiHeader.biBitCount, bitmap->bmiHeader.biCompression));
+ void* raw;
+ BITMAPINFOHEADER info;
+ LONG w = bitmap->bmiHeader.biWidth;
+ LONG h = bitmap->bmiHeader.biHeight;
+ info.biSize = sizeof(BITMAPINFOHEADER);
+ info.biWidth = w;
+ info.biHeight = h;
+ info.biPlanes = 1;
+ info.biBitCount = 32;
+ info.biCompression = BI_RGB;
+ info.biSizeImage = 0;
+ info.biXPelsPerMeter = 1000;
+ info.biYPelsPerMeter = 1000;
+ info.biClrUsed = 0;
+ info.biClrImportant = 0;
+ HDC dc = GetDC(NULL);
+ HBITMAP dst = CreateDIBSection(dc, (BITMAPINFO*)&info,
+ DIB_RGB_COLORS, &raw, NULL, 0);
+
+ // find the start of the pixel data
+ const char* srcBits = (const char*)bitmap + bitmap->bmiHeader.biSize;
+ if (bitmap->bmiHeader.biBitCount >= 16) {
+ if (bitmap->bmiHeader.biCompression == BI_BITFIELDS &&
+ (bitmap->bmiHeader.biBitCount == 16 ||
+ bitmap->bmiHeader.biBitCount == 32)) {
+ srcBits += 3 * sizeof(DWORD);
+ }
+ }
+ else if (bitmap->bmiHeader.biClrUsed != 0) {
+ srcBits += bitmap->bmiHeader.biClrUsed * sizeof(RGBQUAD);
+ }
+ else {
+ //http://msdn.microsoft.com/en-us/library/ke55d167(VS.80).aspx
+ srcBits += (1i64 << bitmap->bmiHeader.biBitCount) * sizeof(RGBQUAD);
+ }
+
+ // copy source image to destination image
+ HDC dstDC = CreateCompatibleDC(dc);
+ HGDIOBJ oldBitmap = SelectObject(dstDC, dst);
+ SetDIBitsToDevice(dstDC, 0, 0, w, h, 0, 0, 0, h,
+ srcBits, bitmap, DIB_RGB_COLORS);
+ SelectObject(dstDC, oldBitmap);
+ DeleteDC(dstDC);
+ GdiFlush();
+
+ // extract data
+ String image((const char*)&info, info.biSize);
+ image.append((const char*)raw, 4 * w * h);
+
+ // clean up GDI
+ DeleteObject(dst);
+ ReleaseDC(NULL, dc);
+
+ // release handle
+ GlobalUnlock(data);
+
+ return image;
+}
diff --git a/src/lib/platform/MSWindowsClipboardBitmapConverter.h b/src/lib/platform/MSWindowsClipboardBitmapConverter.h
new file mode 100644
index 0000000..52b5547
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardBitmapConverter.h
@@ -0,0 +1,36 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/MSWindowsClipboard.h"
+
+//! Convert to/from some text encoding
+class MSWindowsClipboardBitmapConverter :
+ public IMSWindowsClipboardConverter {
+public:
+ MSWindowsClipboardBitmapConverter();
+ virtual ~MSWindowsClipboardBitmapConverter();
+
+ // IMSWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual UINT getWin32Format() const;
+ virtual HANDLE fromIClipboard(const String&) const;
+ virtual String toIClipboard(HANDLE) const;
+};
diff --git a/src/lib/platform/MSWindowsClipboardFacade.cpp b/src/lib/platform/MSWindowsClipboardFacade.cpp
new file mode 100644
index 0000000..3b6478f
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardFacade.cpp
@@ -0,0 +1,31 @@
+/*
+ * barrier -- 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 "platform/MSWindowsClipboardFacade.h"
+
+#include "platform/MSWindowsClipboard.h"
+
+void MSWindowsClipboardFacade::write(HANDLE win32Data, UINT win32Format)
+{
+ if (SetClipboardData(win32Format, win32Data) == NULL) {
+ // free converted data if we couldn't put it on
+ // the clipboard.
+ // nb: couldn't cause this in integ tests.
+ GlobalFree(win32Data);
+ }
+}
diff --git a/src/lib/platform/MSWindowsClipboardFacade.h b/src/lib/platform/MSWindowsClipboardFacade.h
new file mode 100644
index 0000000..a95e835
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardFacade.h
@@ -0,0 +1,29 @@
+/*
+ * barrier -- 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 "platform/IMSWindowsClipboardFacade.h"
+
+#include "barrier/IClipboard.h"
+
+class MSWindowsClipboardFacade : public IMSWindowsClipboardFacade
+{
+public:
+ virtual void write(HANDLE win32Data, UINT win32Format);
+};
diff --git a/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp b/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp
new file mode 100644
index 0000000..347a224
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardHTMLConverter.cpp
@@ -0,0 +1,120 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/MSWindowsClipboardHTMLConverter.h"
+
+#include "base/String.h"
+
+//
+// MSWindowsClipboardHTMLConverter
+//
+
+MSWindowsClipboardHTMLConverter::MSWindowsClipboardHTMLConverter()
+{
+ m_format = RegisterClipboardFormat("HTML Format");
+}
+
+MSWindowsClipboardHTMLConverter::~MSWindowsClipboardHTMLConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+MSWindowsClipboardHTMLConverter::getFormat() const
+{
+ return IClipboard::kHTML;
+}
+
+UINT
+MSWindowsClipboardHTMLConverter::getWin32Format() const
+{
+ return m_format;
+}
+
+String
+MSWindowsClipboardHTMLConverter::doFromIClipboard(const String& data) const
+{
+ // prepare to CF_HTML format prefix and suffix
+ String prefix("Version:0.9\r\nStartHTML:0000000105\r\n"
+ "EndHTML:ZZZZZZZZZZ\r\n"
+ "StartFragment:XXXXXXXXXX\r\nEndFragment:YYYYYYYYYY\r\n"
+ "<!DOCTYPE><HTML><BODY><!--StartFragment-->");
+ String suffix("<!--EndFragment--></BODY></HTML>\r\n");
+
+ // Get byte offsets for header
+ UInt32 StartFragment = (UInt32)prefix.size();
+ UInt32 EndFragment = StartFragment + (UInt32)data.size();
+ // StartHTML is constant by the design of the prefix
+ UInt32 EndHTML = EndFragment + (UInt32)suffix.size();
+
+ prefix.replace(prefix.find("XXXXXXXXXX"), 10,
+ barrier::string::sprintf("%010u", StartFragment));
+ prefix.replace(prefix.find("YYYYYYYYYY"), 10,
+ barrier::string::sprintf("%010u", EndFragment));
+ prefix.replace(prefix.find("ZZZZZZZZZZ"), 10,
+ barrier::string::sprintf("%010u", EndHTML));
+
+ // concatenate
+ prefix += data;
+ prefix += suffix;
+ return prefix;
+}
+
+String
+MSWindowsClipboardHTMLConverter::doToIClipboard(const String& data) const
+{
+ // get fragment start/end args
+ String startArg = findArg(data, "StartFragment");
+ String endArg = findArg(data, "EndFragment");
+ if (startArg.empty() || endArg.empty()) {
+ return String();
+ }
+
+ // convert args to integers
+ SInt32 start = (SInt32)atoi(startArg.c_str());
+ SInt32 end = (SInt32)atoi(endArg.c_str());
+ if (start <= 0 || end <= 0 || start >= end) {
+ return String();
+ }
+
+ // extract the fragment
+ return data.substr(start, end - start);
+}
+
+String
+MSWindowsClipboardHTMLConverter::findArg(
+ const String& data, const String& name) const
+{
+ String::size_type i = data.find(name);
+ if (i == String::npos) {
+ return String();
+ }
+ i = data.find_first_of(":\r\n", i);
+ if (i == String::npos || data[i] != ':') {
+ return String();
+ }
+ i = data.find_first_of("0123456789\r\n", i + 1);
+ if (i == String::npos || data[i] == '\r' || data[i] == '\n') {
+ return String();
+ }
+ String::size_type j = data.find_first_not_of("0123456789", i);
+ if (j == String::npos) {
+ j = data.size();
+ }
+ return data.substr(i, j - i);
+}
diff --git a/src/lib/platform/MSWindowsClipboardHTMLConverter.h b/src/lib/platform/MSWindowsClipboardHTMLConverter.h
new file mode 100644
index 0000000..66c8045
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardHTMLConverter.h
@@ -0,0 +1,45 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/MSWindowsClipboardAnyTextConverter.h"
+
+//! Convert to/from HTML encoding
+class MSWindowsClipboardHTMLConverter :
+ public MSWindowsClipboardAnyTextConverter {
+public:
+ MSWindowsClipboardHTMLConverter();
+ virtual ~MSWindowsClipboardHTMLConverter();
+
+ // IMSWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual UINT getWin32Format() const;
+
+protected:
+ // MSWindowsClipboardAnyTextConverter overrides
+ virtual String doFromIClipboard(const String&) const;
+ virtual String doToIClipboard(const String&) const;
+
+private:
+ String findArg(const String& data, const String& name) const;
+
+private:
+ UINT m_format;
+};
diff --git a/src/lib/platform/MSWindowsClipboardTextConverter.cpp b/src/lib/platform/MSWindowsClipboardTextConverter.cpp
new file mode 100644
index 0000000..360c72c
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardTextConverter.cpp
@@ -0,0 +1,60 @@
+/*
+ * barrier -- 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 "platform/MSWindowsClipboardTextConverter.h"
+
+#include "base/Unicode.h"
+
+//
+// MSWindowsClipboardTextConverter
+//
+
+MSWindowsClipboardTextConverter::MSWindowsClipboardTextConverter()
+{
+ // do nothing
+}
+
+MSWindowsClipboardTextConverter::~MSWindowsClipboardTextConverter()
+{
+ // do nothing
+}
+
+UINT
+MSWindowsClipboardTextConverter::getWin32Format() const
+{
+ return CF_TEXT;
+}
+
+String
+MSWindowsClipboardTextConverter::doFromIClipboard(const String& data) const
+{
+ // convert and add nul terminator
+ return Unicode::UTF8ToText(data) += '\0';
+}
+
+String
+MSWindowsClipboardTextConverter::doToIClipboard(const String& data) const
+{
+ // convert and truncate at first nul terminator
+ String dst = Unicode::textToUTF8(data);
+ String::size_type n = dst.find('\0');
+ if (n != String::npos) {
+ dst.erase(n);
+ }
+ return dst;
+}
diff --git a/src/lib/platform/MSWindowsClipboardTextConverter.h b/src/lib/platform/MSWindowsClipboardTextConverter.h
new file mode 100644
index 0000000..fb081c3
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardTextConverter.h
@@ -0,0 +1,37 @@
+/*
+ * barrier -- 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 "platform/MSWindowsClipboardAnyTextConverter.h"
+
+//! Convert to/from locale text encoding
+class MSWindowsClipboardTextConverter :
+ public MSWindowsClipboardAnyTextConverter {
+public:
+ MSWindowsClipboardTextConverter();
+ virtual ~MSWindowsClipboardTextConverter();
+
+ // IMSWindowsClipboardConverter overrides
+ virtual UINT getWin32Format() const;
+
+protected:
+ // MSWindowsClipboardAnyTextConverter overrides
+ virtual String doFromIClipboard(const String&) const;
+ virtual String doToIClipboard(const String&) const;
+};
diff --git a/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp b/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp
new file mode 100644
index 0000000..0f8642a
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardUTF16Converter.cpp
@@ -0,0 +1,60 @@
+/*
+ * barrier -- 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 "platform/MSWindowsClipboardUTF16Converter.h"
+
+#include "base/Unicode.h"
+
+//
+// MSWindowsClipboardUTF16Converter
+//
+
+MSWindowsClipboardUTF16Converter::MSWindowsClipboardUTF16Converter()
+{
+ // do nothing
+}
+
+MSWindowsClipboardUTF16Converter::~MSWindowsClipboardUTF16Converter()
+{
+ // do nothing
+}
+
+UINT
+MSWindowsClipboardUTF16Converter::getWin32Format() const
+{
+ return CF_UNICODETEXT;
+}
+
+String
+MSWindowsClipboardUTF16Converter::doFromIClipboard(const String& data) const
+{
+ // convert and add nul terminator
+ return Unicode::UTF8ToUTF16(data).append(sizeof(wchar_t), 0);
+}
+
+String
+MSWindowsClipboardUTF16Converter::doToIClipboard(const String& data) const
+{
+ // convert and strip nul terminator
+ String dst = Unicode::UTF16ToUTF8(data);
+ String::size_type n = dst.find('\0');
+ if (n != String::npos) {
+ dst.erase(n);
+ }
+ return dst;
+}
diff --git a/src/lib/platform/MSWindowsClipboardUTF16Converter.h b/src/lib/platform/MSWindowsClipboardUTF16Converter.h
new file mode 100644
index 0000000..e7222bc
--- /dev/null
+++ b/src/lib/platform/MSWindowsClipboardUTF16Converter.h
@@ -0,0 +1,37 @@
+/*
+ * barrier -- 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 "platform/MSWindowsClipboardAnyTextConverter.h"
+
+//! Convert to/from UTF-16 encoding
+class MSWindowsClipboardUTF16Converter :
+ public MSWindowsClipboardAnyTextConverter {
+public:
+ MSWindowsClipboardUTF16Converter();
+ virtual ~MSWindowsClipboardUTF16Converter();
+
+ // IMSWindowsClipboardConverter overrides
+ virtual UINT getWin32Format() const;
+
+protected:
+ // MSWindowsClipboardAnyTextConverter overrides
+ virtual String doFromIClipboard(const String&) const;
+ virtual String doToIClipboard(const String&) const;
+};
diff --git a/src/lib/platform/MSWindowsDebugOutputter.cpp b/src/lib/platform/MSWindowsDebugOutputter.cpp
new file mode 100644
index 0000000..43c38ad
--- /dev/null
+++ b/src/lib/platform/MSWindowsDebugOutputter.cpp
@@ -0,0 +1,58 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "platform/MSWindowsDebugOutputter.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <string>
+
+MSWindowsDebugOutputter::MSWindowsDebugOutputter()
+{
+}
+
+MSWindowsDebugOutputter::~MSWindowsDebugOutputter()
+{
+}
+
+void
+MSWindowsDebugOutputter::open(const char* title)
+{
+}
+
+void
+MSWindowsDebugOutputter::close()
+{
+}
+
+void
+MSWindowsDebugOutputter::show(bool showIfEmpty)
+{
+}
+
+bool
+MSWindowsDebugOutputter::write(ELevel level, const char* msg)
+{
+ OutputDebugString((std::string(msg) + "\n").c_str());
+ return true;
+}
+
+void
+MSWindowsDebugOutputter::flush()
+{
+}
diff --git a/src/lib/platform/MSWindowsDebugOutputter.h b/src/lib/platform/MSWindowsDebugOutputter.h
new file mode 100644
index 0000000..01fd97e
--- /dev/null
+++ b/src/lib/platform/MSWindowsDebugOutputter.h
@@ -0,0 +1,39 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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/ILogOutputter.h"
+
+//! Write log to debugger
+/*!
+This outputter writes output to the debugger. In Visual Studio, this
+can be seen in the Output window.
+*/
+class MSWindowsDebugOutputter : public ILogOutputter {
+public:
+ MSWindowsDebugOutputter();
+ virtual ~MSWindowsDebugOutputter();
+
+ // ILogOutputter overrides
+ virtual void open(const char* title);
+ virtual void close();
+ virtual void show(bool showIfEmpty);
+ virtual bool write(ELevel level, const char* message);
+ virtual void flush();
+};
diff --git a/src/lib/platform/MSWindowsDesks.cpp b/src/lib/platform/MSWindowsDesks.cpp
new file mode 100644
index 0000000..b43a218
--- /dev/null
+++ b/src/lib/platform/MSWindowsDesks.cpp
@@ -0,0 +1,923 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2018 Debauchee Open Source Group
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/MSWindowsDesks.h"
+
+#include "platform/MSWindowsScreen.h"
+#include "barrier/IScreenSaver.h"
+#include "barrier/XScreen.h"
+#include "mt/Lock.h"
+#include "mt/Thread.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "base/IJob.h"
+#include "base/TMethodEventJob.h"
+#include "base/TMethodJob.h"
+#include "base/IEventQueue.h"
+
+#include <malloc.h>
+
+// these are only defined when WINVER >= 0x0500
+#if !defined(SPI_GETMOUSESPEED)
+#define SPI_GETMOUSESPEED 112
+#endif
+#if !defined(SPI_SETMOUSESPEED)
+#define SPI_SETMOUSESPEED 113
+#endif
+#if !defined(SPI_GETSCREENSAVERRUNNING)
+#define SPI_GETSCREENSAVERRUNNING 114
+#endif
+
+// X button stuff
+#if !defined(WM_XBUTTONDOWN)
+#define WM_XBUTTONDOWN 0x020B
+#define WM_XBUTTONUP 0x020C
+#define WM_XBUTTONDBLCLK 0x020D
+#define WM_NCXBUTTONDOWN 0x00AB
+#define WM_NCXBUTTONUP 0x00AC
+#define WM_NCXBUTTONDBLCLK 0x00AD
+#define MOUSEEVENTF_XDOWN 0x0080
+#define MOUSEEVENTF_XUP 0x0100
+#define XBUTTON1 0x0001
+#define XBUTTON2 0x0002
+#endif
+#if !defined(VK_XBUTTON1)
+#define VK_XBUTTON1 0x05
+#define VK_XBUTTON2 0x06
+#endif
+
+// <unused>; <unused>
+#define BARRIER_MSG_SWITCH BARRIER_HOOK_LAST_MSG + 1
+// <unused>; <unused>
+#define BARRIER_MSG_ENTER BARRIER_HOOK_LAST_MSG + 2
+// <unused>; <unused>
+#define BARRIER_MSG_LEAVE BARRIER_HOOK_LAST_MSG + 3
+// wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code
+#define BARRIER_MSG_FAKE_KEY BARRIER_HOOK_LAST_MSG + 4
+ // flags, XBUTTON id
+#define BARRIER_MSG_FAKE_BUTTON BARRIER_HOOK_LAST_MSG + 5
+// x; y
+#define BARRIER_MSG_FAKE_MOVE BARRIER_HOOK_LAST_MSG + 6
+// xDelta; yDelta
+#define BARRIER_MSG_FAKE_WHEEL BARRIER_HOOK_LAST_MSG + 7
+// POINT*; <unused>
+#define BARRIER_MSG_CURSOR_POS BARRIER_HOOK_LAST_MSG + 8
+// IKeyState*; <unused>
+#define BARRIER_MSG_SYNC_KEYS BARRIER_HOOK_LAST_MSG + 9
+// install; <unused>
+#define BARRIER_MSG_SCREENSAVER BARRIER_HOOK_LAST_MSG + 10
+// dx; dy
+#define BARRIER_MSG_FAKE_REL_MOVE BARRIER_HOOK_LAST_MSG + 11
+// enable; <unused>
+#define BARRIER_MSG_FAKE_INPUT BARRIER_HOOK_LAST_MSG + 12
+
+//
+// MSWindowsDesks
+//
+
+MSWindowsDesks::MSWindowsDesks(
+ bool isPrimary, bool noHooks,
+ const IScreenSaver* screensaver, IEventQueue* events,
+ IJob* updateKeys, bool stopOnDeskSwitch) :
+ m_isPrimary(isPrimary),
+ m_noHooks(noHooks),
+ m_isOnScreen(m_isPrimary),
+ m_x(0), m_y(0),
+ m_w(0), m_h(0),
+ m_xCenter(0), m_yCenter(0),
+ m_multimon(false),
+ m_timer(NULL),
+ m_screensaver(screensaver),
+ m_screensaverNotify(false),
+ m_activeDesk(NULL),
+ m_activeDeskName(),
+ m_mutex(),
+ m_deskReady(&m_mutex, false),
+ m_updateKeys(updateKeys),
+ m_events(events),
+ m_stopOnDeskSwitch(stopOnDeskSwitch)
+{
+ m_cursor = createBlankCursor();
+ m_deskClass = createDeskWindowClass(m_isPrimary);
+ m_keyLayout = GetKeyboardLayout(GetCurrentThreadId());
+ resetOptions();
+}
+
+MSWindowsDesks::~MSWindowsDesks()
+{
+ disable();
+ destroyClass(m_deskClass);
+ destroyCursor(m_cursor);
+ delete m_updateKeys;
+}
+
+void
+MSWindowsDesks::enable()
+{
+ m_threadID = GetCurrentThreadId();
+
+ // set the active desk and (re)install the hooks
+ checkDesk();
+
+ // install the desk timer. this timer periodically checks
+ // which desk is active and reinstalls the hooks as necessary.
+ // we wouldn't need this if windows notified us of a desktop
+ // change but as far as i can tell it doesn't.
+ m_timer = m_events->newTimer(0.2, NULL);
+ m_events->adoptHandler(Event::kTimer, m_timer,
+ new TMethodEventJob<MSWindowsDesks>(
+ this, &MSWindowsDesks::handleCheckDesk));
+
+ updateKeys();
+}
+
+void
+MSWindowsDesks::disable()
+{
+ // remove timer
+ if (m_timer != NULL) {
+ m_events->removeHandler(Event::kTimer, m_timer);
+ m_events->deleteTimer(m_timer);
+ m_timer = NULL;
+ }
+
+ // destroy desks
+ removeDesks();
+
+ m_isOnScreen = m_isPrimary;
+}
+
+void
+MSWindowsDesks::enter()
+{
+ sendMessage(BARRIER_MSG_ENTER, 0, 0);
+}
+
+void
+MSWindowsDesks::leave(HKL keyLayout)
+{
+ sendMessage(BARRIER_MSG_LEAVE, (WPARAM)keyLayout, 0);
+}
+
+void
+MSWindowsDesks::resetOptions()
+{
+ m_leaveForegroundOption = false;
+}
+
+void
+MSWindowsDesks::setOptions(const OptionsList& options)
+{
+ for (UInt32 i = 0, n = (UInt32)options.size(); i < n; i += 2) {
+ if (options[i] == kOptionWin32KeepForeground) {
+ m_leaveForegroundOption = (options[i + 1] != 0);
+ LOG((CLOG_DEBUG1 "%s the foreground window", m_leaveForegroundOption ? "don\'t grab" : "grab"));
+ }
+ }
+}
+
+void
+MSWindowsDesks::updateKeys()
+{
+ sendMessage(BARRIER_MSG_SYNC_KEYS, 0, 0);
+}
+
+void
+MSWindowsDesks::setShape(SInt32 x, SInt32 y,
+ SInt32 width, SInt32 height,
+ SInt32 xCenter, SInt32 yCenter, bool isMultimon)
+{
+ m_x = x;
+ m_y = y;
+ m_w = width;
+ m_h = height;
+ m_xCenter = xCenter;
+ m_yCenter = yCenter;
+ m_multimon = isMultimon;
+}
+
+void
+MSWindowsDesks::installScreensaverHooks(bool install)
+{
+ if (m_isPrimary && m_screensaverNotify != install) {
+ m_screensaverNotify = install;
+ sendMessage(BARRIER_MSG_SCREENSAVER, install, 0);
+ }
+}
+
+void
+MSWindowsDesks::fakeInputBegin()
+{
+ sendMessage(BARRIER_MSG_FAKE_INPUT, 1, 0);
+}
+
+void
+MSWindowsDesks::fakeInputEnd()
+{
+ sendMessage(BARRIER_MSG_FAKE_INPUT, 0, 0);
+}
+
+void
+MSWindowsDesks::getCursorPos(SInt32& x, SInt32& y) const
+{
+ POINT pos;
+ sendMessage(BARRIER_MSG_CURSOR_POS, reinterpret_cast<WPARAM>(&pos), 0);
+ x = pos.x;
+ y = pos.y;
+}
+
+void
+MSWindowsDesks::fakeKeyEvent(
+ KeyButton button, UINT virtualKey,
+ bool press, bool /*isAutoRepeat*/) const
+{
+ // synthesize event
+ DWORD flags = 0;
+ if (((button & 0x100u) != 0)) {
+ flags |= KEYEVENTF_EXTENDEDKEY;
+ }
+ if (!press) {
+ flags |= KEYEVENTF_KEYUP;
+ }
+ sendMessage(BARRIER_MSG_FAKE_KEY, flags,
+ MAKEWORD(static_cast<BYTE>(button & 0xffu),
+ static_cast<BYTE>(virtualKey & 0xffu)));
+}
+
+void
+MSWindowsDesks::fakeMouseButton(ButtonID button, bool press)
+{
+ // the system will swap the meaning of left/right for us if
+ // the user has configured a left-handed mouse but we don't
+ // want it to swap since we want the handedness of the
+ // server's mouse. so pre-swap for a left-handed mouse.
+ if (GetSystemMetrics(SM_SWAPBUTTON)) {
+ switch (button) {
+ case kButtonLeft:
+ button = kButtonRight;
+ break;
+
+ case kButtonRight:
+ button = kButtonLeft;
+ break;
+ }
+ }
+
+ // map button id to button flag and button data
+ DWORD data = 0;
+ DWORD flags;
+ switch (button) {
+ case kButtonLeft:
+ flags = press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
+ break;
+
+ case kButtonMiddle:
+ flags = press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
+ break;
+
+ case kButtonRight:
+ flags = press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
+ break;
+
+ case kButtonExtra0 + 0:
+ data = XBUTTON1;
+ flags = press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
+ break;
+
+ case kButtonExtra0 + 1:
+ data = XBUTTON2;
+ flags = press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
+ break;
+
+ default:
+ return;
+ }
+
+ // do it
+ sendMessage(BARRIER_MSG_FAKE_BUTTON, flags, data);
+}
+
+void
+MSWindowsDesks::fakeMouseMove(SInt32 x, SInt32 y) const
+{
+ sendMessage(BARRIER_MSG_FAKE_MOVE,
+ static_cast<WPARAM>(x),
+ static_cast<LPARAM>(y));
+}
+
+void
+MSWindowsDesks::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
+{
+ sendMessage(BARRIER_MSG_FAKE_REL_MOVE,
+ static_cast<WPARAM>(dx),
+ static_cast<LPARAM>(dy));
+}
+
+void
+MSWindowsDesks::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
+{
+ sendMessage(BARRIER_MSG_FAKE_WHEEL, xDelta, yDelta);
+}
+
+void
+MSWindowsDesks::sendMessage(UINT msg, WPARAM wParam, LPARAM lParam) const
+{
+ if (m_activeDesk != NULL && m_activeDesk->m_window != NULL) {
+ PostThreadMessage(m_activeDesk->m_threadID, msg, wParam, lParam);
+ waitForDesk();
+ }
+}
+
+HCURSOR
+MSWindowsDesks::createBlankCursor() const
+{
+ // create a transparent cursor
+ int cw = GetSystemMetrics(SM_CXCURSOR);
+ int ch = GetSystemMetrics(SM_CYCURSOR);
+ UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)];
+ UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)];
+ memset(cursorAND, 0xff, ch * ((cw + 31) >> 2));
+ memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2));
+ HCURSOR c = CreateCursor(MSWindowsScreen::getWindowInstance(),
+ 0, 0, cw, ch, cursorAND, cursorXOR);
+ delete[] cursorXOR;
+ delete[] cursorAND;
+ return c;
+}
+
+void
+MSWindowsDesks::destroyCursor(HCURSOR cursor) const
+{
+ if (cursor != NULL) {
+ DestroyCursor(cursor);
+ }
+}
+
+ATOM
+MSWindowsDesks::createDeskWindowClass(bool isPrimary) const
+{
+ WNDCLASSEX classInfo;
+ classInfo.cbSize = sizeof(classInfo);
+ classInfo.style = CS_DBLCLKS | CS_NOCLOSE;
+ classInfo.lpfnWndProc = isPrimary ?
+ &MSWindowsDesks::primaryDeskProc :
+ &MSWindowsDesks::secondaryDeskProc;
+ classInfo.cbClsExtra = 0;
+ classInfo.cbWndExtra = 0;
+ classInfo.hInstance = MSWindowsScreen::getWindowInstance();
+ classInfo.hIcon = NULL;
+ classInfo.hCursor = m_cursor;
+ classInfo.hbrBackground = NULL;
+ classInfo.lpszMenuName = NULL;
+ classInfo.lpszClassName = "BarrierDesk";
+ classInfo.hIconSm = NULL;
+ return RegisterClassEx(&classInfo);
+}
+
+void
+MSWindowsDesks::destroyClass(ATOM windowClass) const
+{
+ if (windowClass != 0) {
+ UnregisterClass(MAKEINTATOM(windowClass),
+ MSWindowsScreen::getWindowInstance());
+ }
+}
+
+HWND
+MSWindowsDesks::createWindow(ATOM windowClass, const char* name) const
+{
+ HWND window = CreateWindowEx(WS_EX_TRANSPARENT |
+ WS_EX_TOOLWINDOW,
+ MAKEINTATOM(windowClass),
+ name,
+ WS_POPUP,
+ 0, 0, 1, 1,
+ NULL, NULL,
+ MSWindowsScreen::getWindowInstance(),
+ NULL);
+ if (window == NULL) {
+ LOG((CLOG_ERR "failed to create window: %d", GetLastError()));
+ throw XScreenOpenFailure();
+ }
+ return window;
+}
+
+void
+MSWindowsDesks::destroyWindow(HWND hwnd) const
+{
+ if (hwnd != NULL) {
+ DestroyWindow(hwnd);
+ }
+}
+
+LRESULT CALLBACK
+MSWindowsDesks::primaryDeskProc(
+ HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+LRESULT CALLBACK
+MSWindowsDesks::secondaryDeskProc(
+ HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ // would like to detect any local user input and hide the hider
+ // window but for now we just detect mouse motion.
+ bool hide = false;
+ switch (msg) {
+ case WM_MOUSEMOVE:
+ if (LOWORD(lParam) != 0 || HIWORD(lParam) != 0) {
+ hide = true;
+ }
+ break;
+ }
+
+ if (hide && IsWindowVisible(hwnd)) {
+ ReleaseCapture();
+ SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE |
+ SWP_NOACTIVATE | SWP_HIDEWINDOW);
+ }
+
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+}
+
+void
+MSWindowsDesks::deskMouseMove(SInt32 x, SInt32 y) const
+{
+ // when using absolute positioning with mouse_event(),
+ // the normalized device coordinates range over only
+ // the primary screen.
+ SInt32 w = GetSystemMetrics(SM_CXSCREEN);
+ SInt32 h = GetSystemMetrics(SM_CYSCREEN);
+ mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
+ (DWORD)((65535.0f * x) / (w - 1) + 0.5f),
+ (DWORD)((65535.0f * y) / (h - 1) + 0.5f),
+ 0, 0);
+}
+
+void
+MSWindowsDesks::deskMouseRelativeMove(SInt32 dx, SInt32 dy) const
+{
+ // relative moves are subject to cursor acceleration which we don't
+ // want.so we disable acceleration, do the relative move, then
+ // restore acceleration. there's a slight chance we'll end up in
+ // the wrong place if the user moves the cursor using this system's
+ // mouse while simultaneously moving the mouse on the server
+ // system. that defeats the purpose of barrier so we'll assume
+ // that won't happen. even if it does, the next mouse move will
+ // correct the position.
+
+ // save mouse speed & acceleration
+ int oldSpeed[4];
+ bool accelChanged =
+ SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) &&
+ SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0);
+
+ // use 1:1 motion
+ if (accelChanged) {
+ int newSpeed[4] = { 0, 0, 0, 1 };
+ accelChanged =
+ SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) ||
+ SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0);
+ }
+
+ // move relative to mouse position
+ mouse_event(MOUSEEVENTF_MOVE, dx, dy, 0, 0);
+
+ // restore mouse speed & acceleration
+ if (accelChanged) {
+ SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0);
+ SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0);
+ }
+}
+
+void
+MSWindowsDesks::deskEnter(Desk* desk)
+{
+ if (!m_isPrimary) {
+ ReleaseCapture();
+ }
+ ShowCursor(TRUE);
+ SetWindowPos(desk->m_window, HWND_BOTTOM, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE |
+ SWP_NOACTIVATE | SWP_HIDEWINDOW);
+
+ // restore the foreground window
+ // XXX -- this raises the window to the top of the Z-order. we
+ // want it to stay wherever it was to properly support X-mouse
+ // (mouse over activation) but i've no idea how to do that.
+ // the obvious workaround of using SetWindowPos() to move it back
+ // after being raised doesn't work.
+ DWORD thisThread =
+ GetWindowThreadProcessId(desk->m_window, NULL);
+ DWORD thatThread =
+ GetWindowThreadProcessId(desk->m_foregroundWindow, NULL);
+ AttachThreadInput(thatThread, thisThread, TRUE);
+ SetForegroundWindow(desk->m_foregroundWindow);
+ AttachThreadInput(thatThread, thisThread, FALSE);
+ EnableWindow(desk->m_window, FALSE);
+ desk->m_foregroundWindow = NULL;
+}
+
+void
+MSWindowsDesks::deskLeave(Desk* desk, HKL keyLayout)
+{
+ ShowCursor(FALSE);
+ if (m_isPrimary) {
+ // map a window to hide the cursor and to use whatever keyboard
+ // layout we choose rather than the keyboard layout of the last
+ // active window.
+ int x, y, w, h;
+ // with a low level hook the cursor will never budge so
+ // just a 1x1 window is sufficient.
+ x = m_xCenter;
+ y = m_yCenter;
+ w = 1;
+ h = 1;
+ SetWindowPos(desk->m_window, HWND_TOP, x, y, w, h,
+ SWP_NOACTIVATE | SWP_SHOWWINDOW);
+
+ // since we're using low-level hooks, disable the foreground window
+ // so it can't mess up any of our keyboard events. the console
+ // program, for example, will cause characters to be reported as
+ // unshifted, regardless of the shift key state. interestingly
+ // we do see the shift key go down and up.
+ //
+ // note that we must enable the window to activate it and we
+ // need to disable the window on deskEnter.
+ desk->m_foregroundWindow = getForegroundWindow();
+ if (desk->m_foregroundWindow != NULL) {
+ EnableWindow(desk->m_window, TRUE);
+ SetActiveWindow(desk->m_window);
+ DWORD thisThread =
+ 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
+ SetWindowPos(desk->m_window, HWND_TOP,
+ m_xCenter, m_yCenter, 1, 1,
+ SWP_NOACTIVATE | SWP_SHOWWINDOW);
+
+ // watch for mouse motion. if we see any then we hide the
+ // hider window so the user can use the physically attached
+ // mouse if desired. we'd rather not capture the mouse but
+ // we aren't notified when the mouse leaves our window.
+ SetCapture(desk->m_window);
+
+ // warp the mouse to the cursor center
+ LOG((CLOG_DEBUG2 "warping cursor to center: %+d,%+d", m_xCenter, m_yCenter));
+ deskMouseMove(m_xCenter, m_yCenter);
+ }
+}
+
+void
+MSWindowsDesks::deskThread(void* vdesk)
+{
+ MSG msg;
+
+ // use given desktop for this thread
+ Desk* desk = static_cast<Desk*>(vdesk);
+ desk->m_threadID = GetCurrentThreadId();
+ desk->m_window = NULL;
+ desk->m_foregroundWindow = NULL;
+ if (desk->m_desk != NULL && SetThreadDesktop(desk->m_desk) != 0) {
+ // create a message queue
+ PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE);
+
+ // create a window. we use this window to hide the cursor.
+ try {
+ desk->m_window = createWindow(m_deskClass, "BarrierDesk");
+ LOG((CLOG_DEBUG "desk %s window is 0x%08x", desk->m_name.c_str(), desk->m_window));
+ }
+ catch (...) {
+ // ignore
+ LOG((CLOG_DEBUG "can't create desk window for %s", desk->m_name.c_str()));
+ }
+ }
+
+ // tell main thread that we're ready
+ {
+ Lock lock(&m_mutex);
+ m_deskReady = true;
+ m_deskReady.broadcast();
+ }
+
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ switch (msg.message) {
+ default:
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ continue;
+
+ case BARRIER_MSG_SWITCH:
+ if (m_isPrimary && !m_noHooks) {
+ MSWindowsHook::uninstall();
+ if (m_screensaverNotify) {
+ MSWindowsHook::uninstallScreenSaver();
+ MSWindowsHook::installScreenSaver();
+ }
+ if (!MSWindowsHook::install()) {
+ // we won't work on this desk
+ LOG((CLOG_DEBUG "Cannot hook on this desk"));
+ }
+ // a window on the primary screen with low-level hooks
+ // should never activate.
+ if (desk->m_window)
+ EnableWindow(desk->m_window, FALSE);
+ }
+ break;
+
+ case BARRIER_MSG_ENTER:
+ m_isOnScreen = true;
+ deskEnter(desk);
+ break;
+
+ case BARRIER_MSG_LEAVE:
+ m_isOnScreen = false;
+ m_keyLayout = (HKL)msg.wParam;
+ deskLeave(desk, m_keyLayout);
+ break;
+
+ case BARRIER_MSG_FAKE_KEY:
+ keybd_event(HIBYTE(msg.lParam), LOBYTE(msg.lParam), (DWORD)msg.wParam, 0);
+ break;
+
+ case BARRIER_MSG_FAKE_BUTTON:
+ if (msg.wParam != 0) {
+ mouse_event((DWORD)msg.wParam, 0, 0, (DWORD)msg.lParam, 0);
+ }
+ break;
+
+ case BARRIER_MSG_FAKE_MOVE:
+ deskMouseMove(static_cast<SInt32>(msg.wParam),
+ static_cast<SInt32>(msg.lParam));
+ break;
+
+ case BARRIER_MSG_FAKE_REL_MOVE:
+ deskMouseRelativeMove(static_cast<SInt32>(msg.wParam),
+ static_cast<SInt32>(msg.lParam));
+ break;
+
+ case BARRIER_MSG_FAKE_WHEEL:
+ // XXX -- add support for x-axis scrolling
+ if (msg.lParam != 0) {
+ mouse_event(MOUSEEVENTF_WHEEL, 0, 0, (DWORD)msg.lParam, 0);
+ }
+ break;
+
+ case BARRIER_MSG_CURSOR_POS: {
+ POINT* pos = reinterpret_cast<POINT*>(msg.wParam);
+ if (!GetCursorPos(pos)) {
+ pos->x = m_xCenter;
+ pos->y = m_yCenter;
+ }
+ break;
+ }
+
+ case BARRIER_MSG_SYNC_KEYS:
+ m_updateKeys->run();
+ break;
+
+ case BARRIER_MSG_SCREENSAVER:
+ if (!m_noHooks) {
+ if (msg.wParam != 0) {
+ MSWindowsHook::installScreenSaver();
+ }
+ else {
+ MSWindowsHook::uninstallScreenSaver();
+ }
+ }
+ break;
+
+ case BARRIER_MSG_FAKE_INPUT:
+ keybd_event(BARRIER_HOOK_FAKE_INPUT_VIRTUAL_KEY,
+ BARRIER_HOOK_FAKE_INPUT_SCANCODE,
+ msg.wParam ? 0 : KEYEVENTF_KEYUP, 0);
+ break;
+ }
+
+ // notify that message was processed
+ Lock lock(&m_mutex);
+ m_deskReady = true;
+ m_deskReady.broadcast();
+ }
+
+ // clean up
+ deskEnter(desk);
+ if (desk->m_window != NULL) {
+ DestroyWindow(desk->m_window);
+ }
+ if (desk->m_desk != NULL) {
+ closeDesktop(desk->m_desk);
+ }
+}
+
+MSWindowsDesks::Desk*
+MSWindowsDesks::addDesk(const String& name, HDESK hdesk)
+{
+ Desk* desk = new Desk;
+ desk->m_name = name;
+ desk->m_desk = hdesk;
+ desk->m_targetID = GetCurrentThreadId();
+ desk->m_thread = new Thread(new TMethodJob<MSWindowsDesks>(
+ this, &MSWindowsDesks::deskThread, desk));
+ waitForDesk();
+ m_desks.insert(std::make_pair(name, desk));
+ return desk;
+}
+
+void
+MSWindowsDesks::removeDesks()
+{
+ for (Desks::iterator index = m_desks.begin();
+ index != m_desks.end(); ++index) {
+ Desk* desk = index->second;
+ PostThreadMessage(desk->m_threadID, WM_QUIT, 0, 0);
+ desk->m_thread->wait();
+ delete desk->m_thread;
+ delete desk;
+ }
+ m_desks.clear();
+ m_activeDesk = NULL;
+ m_activeDeskName = "";
+}
+
+void
+MSWindowsDesks::checkDesk()
+{
+ // get current desktop. if we already know about it then return.
+ Desk* desk;
+ HDESK hdesk = openInputDesktop();
+ String name = getDesktopName(hdesk);
+ Desks::const_iterator index = m_desks.find(name);
+ if (index == m_desks.end()) {
+ desk = addDesk(name, hdesk);
+ // hold on to hdesk until thread exits so the desk can't
+ // be removed by the system
+ }
+ else {
+ closeDesktop(hdesk);
+ desk = index->second;
+ }
+
+ // if we are told to shut down on desk switch, and this is not the
+ // first switch, then shut down.
+ if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName) {
+ LOG((CLOG_DEBUG "shutting down because of desk switch to \"%s\"", name.c_str()));
+ m_events->addEvent(Event(Event::kQuit));
+ return;
+ }
+
+ // if active desktop changed then tell the old and new desk threads
+ // about the change. don't switch desktops when the screensaver is
+ // active becaue we'd most likely switch to the screensaver desktop
+ // which would have the side effect of forcing the screensaver to
+ // stop.
+ if (name != m_activeDeskName && !m_screensaver->isActive()) {
+ // show cursor on previous desk
+ bool wasOnScreen = m_isOnScreen;
+ if (!wasOnScreen) {
+ sendMessage(BARRIER_MSG_ENTER, 0, 0);
+ }
+
+ // check for desk accessibility change. we don't get events
+ // from an inaccessible desktop so when we switch from an
+ // inaccessible desktop to an accessible one we have to
+ // update the keyboard state.
+ LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str()));
+ bool syncKeys = false;
+ bool isAccessible = isDeskAccessible(desk);
+ if (isDeskAccessible(m_activeDesk) != isAccessible) {
+ if (isAccessible) {
+ LOG((CLOG_DEBUG "desktop is now accessible"));
+ syncKeys = true;
+ }
+ else {
+ LOG((CLOG_DEBUG "desktop is now inaccessible"));
+ }
+ }
+
+ // switch desk
+ m_activeDesk = desk;
+ m_activeDeskName = name;
+ sendMessage(BARRIER_MSG_SWITCH, 0, 0);
+
+ // hide cursor on new desk
+ if (!wasOnScreen) {
+ sendMessage(BARRIER_MSG_LEAVE, (WPARAM)m_keyLayout, 0);
+ }
+
+ // update keys if necessary
+ if (syncKeys) {
+ updateKeys();
+ }
+ }
+ else if (name != m_activeDeskName) {
+ // screen saver might have started
+ PostThreadMessage(m_threadID, BARRIER_MSG_SCREEN_SAVER, TRUE, 0);
+ }
+}
+
+bool
+MSWindowsDesks::isDeskAccessible(const Desk* desk) const
+{
+ return (desk != NULL && desk->m_desk != NULL);
+}
+
+void
+MSWindowsDesks::waitForDesk() const
+{
+ MSWindowsDesks* self = const_cast<MSWindowsDesks*>(this);
+
+ Lock lock(&m_mutex);
+ while (!(bool)m_deskReady) {
+ m_deskReady.wait();
+ }
+ self->m_deskReady = false;
+}
+
+void
+MSWindowsDesks::handleCheckDesk(const Event&, void*)
+{
+ checkDesk();
+
+ // also check if screen saver is running if on a modern OS and
+ // this is the primary screen.
+ if (m_isPrimary) {
+ BOOL running;
+ SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, FALSE);
+ PostThreadMessage(m_threadID, BARRIER_MSG_SCREEN_SAVER, running, 0);
+ }
+}
+
+HDESK
+MSWindowsDesks::openInputDesktop()
+{
+ return OpenInputDesktop(
+ DF_ALLOWOTHERACCOUNTHOOK, TRUE,
+ DESKTOP_CREATEWINDOW | DESKTOP_HOOKCONTROL | GENERIC_WRITE);
+}
+
+void
+MSWindowsDesks::closeDesktop(HDESK desk)
+{
+ if (desk != NULL) {
+ CloseDesktop(desk);
+ }
+}
+
+String
+MSWindowsDesks::getDesktopName(HDESK desk)
+{
+ if (desk == NULL) {
+ return String();
+ }
+ else {
+ DWORD size;
+ GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size);
+ TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR));
+ GetUserObjectInformation(desk, UOI_NAME, name, size, &size);
+ String result(name);
+ return result;
+ }
+}
+
+HWND
+MSWindowsDesks::getForegroundWindow() const
+{
+ // Ideally we'd return NULL as much as possible, only returning
+ // the actual foreground window when we know it's going to mess
+ // up our keyboard input. For now we'll just let the user
+ // decide.
+ if (m_leaveForegroundOption) {
+ return NULL;
+ }
+ return GetForegroundWindow();
+}
diff --git a/src/lib/platform/MSWindowsDesks.h b/src/lib/platform/MSWindowsDesks.h
new file mode 100644
index 0000000..da93c34
--- /dev/null
+++ b/src/lib/platform/MSWindowsDesks.h
@@ -0,0 +1,297 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2018 Debauchee Open Source Group
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/synwinhk.h"
+#include "barrier/key_types.h"
+#include "barrier/mouse_types.h"
+#include "barrier/option_types.h"
+#include "mt/CondVar.h"
+#include "mt/Mutex.h"
+#include "base/String.h"
+#include "common/stdmap.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+class Event;
+class EventQueueTimer;
+class Thread;
+class IJob;
+class IScreenSaver;
+class IEventQueue;
+
+//! Microsoft Windows desk handling
+/*!
+Desks in Microsoft Windows are only remotely like desktops on X11
+systems. A desk is another virtual surface for windows but desks
+impose serious restrictions: a thread can interact with only one
+desk at a time, you can't switch desks if the thread has any hooks
+installed or owns any windows, windows cannot exist on multiple
+desks at once, etc. Basically, they're useless except for running
+the login window or the screensaver, which is what they're used
+for. Barrier must deal with them mainly because of the login
+window and screensaver but users can create their own desks and
+barrier should work on those too.
+
+This class encapsulates all the desk nastiness. Clients of this
+object don't have to know anything about desks.
+*/
+class MSWindowsDesks {
+public:
+ //! Constructor
+ /*!
+ \p isPrimary is true iff the desk is for a primary screen.
+ \p screensaver points to a screensaver object and it's used
+ only to check if the screensaver is active. The \p updateKeys
+ job is adopted and is called when the key state should be
+ updated in a thread attached to the current desk.
+ \p hookLibrary must be a handle to the hook library.
+ */
+ MSWindowsDesks(
+ bool isPrimary, bool noHooks,
+ const IScreenSaver* screensaver, IEventQueue* events,
+ IJob* updateKeys, bool stopOnDeskSwitch);
+ ~MSWindowsDesks();
+
+ //! @name manipulators
+ //@{
+
+ //! Enable desk tracking
+ /*!
+ Enables desk tracking. While enabled, this object checks to see
+ if the desk has changed and ensures that the hooks are installed
+ on the new desk. \c setShape should be called at least once
+ before calling \c enable.
+ */
+ void enable();
+
+ //! Disable desk tracking
+ /*!
+ Disables desk tracking. \sa enable.
+ */
+ void disable();
+
+ //! Notify of entering a desk
+ /*!
+ Prepares a desk for when the cursor enters it.
+ */
+ void enter();
+
+ //! Notify of leaving a desk
+ /*!
+ Prepares a desk for when the cursor leaves it.
+ */
+ void leave(HKL keyLayout);
+
+ //! Notify of options changes
+ /*!
+ Resets all options to their default values.
+ */
+ void resetOptions();
+
+ //! Notify of options changes
+ /*!
+ Set options to given values. Ignores unknown options and doesn't
+ modify options that aren't given in \c options.
+ */
+ void setOptions(const OptionsList& options);
+
+ //! Update the key state
+ /*!
+ Causes the key state to get updated to reflect the physical keyboard
+ state and current keyboard mapping.
+ */
+ void updateKeys();
+
+ //! Tell desk about new size
+ /*!
+ This tells the desks that the display size has changed.
+ */
+ void setShape(SInt32 x, SInt32 y,
+ SInt32 width, SInt32 height,
+ SInt32 xCenter, SInt32 yCenter, bool isMultimon);
+
+ //! Install/uninstall screensaver hooks
+ /*!
+ If \p install is true then the screensaver hooks are installed and,
+ if desk tracking is enabled, updated whenever the desk changes. If
+ \p install is false then the screensaver hooks are uninstalled.
+ */
+ void installScreensaverHooks(bool install);
+
+ //! Start ignoring user input
+ /*!
+ Starts ignoring user input so we don't pick up our own synthesized events.
+ */
+ void fakeInputBegin();
+
+ //! Stop ignoring user input
+ /*!
+ Undoes whatever \c fakeInputBegin() did.
+ */
+ void fakeInputEnd();
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get cursor position
+ /*!
+ Return the current position of the cursor in \c x and \c y.
+ */
+ void getCursorPos(SInt32& x, SInt32& y) const;
+
+ //! Fake key press/release
+ /*!
+ Synthesize a press or release of key \c button.
+ */
+ void fakeKeyEvent(KeyButton button, UINT virtualKey,
+ bool press, bool isAutoRepeat) const;
+
+ //! Fake mouse press/release
+ /*!
+ Synthesize a press or release of mouse button \c id.
+ */
+ void fakeMouseButton(ButtonID id, bool press);
+
+ //! Fake mouse move
+ /*!
+ Synthesize a mouse move to the absolute coordinates \c x,y.
+ */
+ void fakeMouseMove(SInt32 x, SInt32 y) const;
+
+ //! Fake mouse move
+ /*!
+ Synthesize a mouse move to the relative coordinates \c dx,dy.
+ */
+ void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const;
+
+ //! Fake mouse wheel
+ /*!
+ Synthesize a mouse wheel event of amount \c delta in direction \c axis.
+ */
+ void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
+
+ //@}
+
+private:
+ class Desk {
+ public:
+ String m_name;
+ Thread* m_thread;
+ DWORD m_threadID;
+ DWORD m_targetID;
+ HDESK m_desk;
+ HWND m_window;
+ HWND m_foregroundWindow;
+ bool m_lowLevel;
+ };
+ typedef std::map<String, Desk*> Desks;
+
+ // initialization and shutdown operations
+ HCURSOR createBlankCursor() const;
+ void destroyCursor(HCURSOR cursor) const;
+ ATOM createDeskWindowClass(bool isPrimary) const;
+ void destroyClass(ATOM windowClass) const;
+ HWND createWindow(ATOM windowClass, const char* name) const;
+ void destroyWindow(HWND) const;
+
+ // message handlers
+ void deskMouseMove(SInt32 x, SInt32 y) const;
+ void deskMouseRelativeMove(SInt32 dx, SInt32 dy) const;
+ void deskEnter(Desk* desk);
+ void deskLeave(Desk* desk, HKL keyLayout);
+ void deskThread(void* vdesk);
+
+ // desk switch checking and handling
+ Desk* addDesk(const String& name, HDESK hdesk);
+ void removeDesks();
+ void checkDesk();
+ bool isDeskAccessible(const Desk* desk) const;
+ void handleCheckDesk(const Event& event, void*);
+
+ // communication with desk threads
+ void waitForDesk() const;
+ void sendMessage(UINT, WPARAM, LPARAM) const;
+
+ // work around for messed up keyboard events from low-level hooks
+ HWND getForegroundWindow() const;
+
+ // desk API wrappers
+ HDESK openInputDesktop();
+ void closeDesktop(HDESK);
+ String getDesktopName(HDESK);
+
+ // our desk window procs
+ static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM);
+ static LRESULT CALLBACK secondaryDeskProc(HWND, UINT, WPARAM, LPARAM);
+
+private:
+ // true if screen is being used as a primary screen, false otherwise
+ bool m_isPrimary;
+
+ // true if hooks are not to be installed (useful for debugging)
+ bool m_noHooks;
+
+ // true if mouse has entered the screen
+ bool m_isOnScreen;
+
+ // our resources
+ ATOM m_deskClass;
+ HCURSOR m_cursor;
+
+ // screen shape stuff
+ SInt32 m_x, m_y;
+ SInt32 m_w, m_h;
+ SInt32 m_xCenter, m_yCenter;
+
+ // true if system appears to have multiple monitors
+ bool m_multimon;
+
+ // the timer used to check for desktop switching
+ EventQueueTimer* m_timer;
+
+ // screen saver stuff
+ DWORD m_threadID;
+ const IScreenSaver* m_screensaver;
+ bool m_screensaverNotify;
+
+ // the current desk and it's name
+ Desk* m_activeDesk;
+ String m_activeDeskName;
+
+ // one desk per desktop and a cond var to communicate with it
+ Mutex m_mutex;
+ CondVar<bool> m_deskReady;
+ Desks m_desks;
+
+ // keyboard stuff
+ IJob* m_updateKeys;
+ HKL m_keyLayout;
+
+ // options
+ bool m_leaveForegroundOption;
+
+ IEventQueue* m_events;
+
+ // true if program should stop on desk switch.
+ bool m_stopOnDeskSwitch;
+};
diff --git a/src/lib/platform/MSWindowsDropTarget.cpp b/src/lib/platform/MSWindowsDropTarget.cpp
new file mode 100644
index 0000000..d647808
--- /dev/null
+++ b/src/lib/platform/MSWindowsDropTarget.cpp
@@ -0,0 +1,178 @@
+/*
+ * barrier -- 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 "platform/MSWindowsDropTarget.h"
+
+#include "base/Log.h"
+#include "common/common.h"
+
+#include <stdio.h>
+#include <Shlobj.h>
+
+void getDropData(IDataObject *pDataObject);
+
+MSWindowsDropTarget* MSWindowsDropTarget::s_instance = NULL;
+
+MSWindowsDropTarget::MSWindowsDropTarget() :
+ m_refCount(1),
+ m_allowDrop(false)
+{
+ s_instance = this;
+}
+
+MSWindowsDropTarget::~MSWindowsDropTarget()
+{
+}
+
+MSWindowsDropTarget&
+MSWindowsDropTarget::instance()
+{
+ assert(s_instance != NULL);
+ return *s_instance;
+}
+
+HRESULT
+MSWindowsDropTarget::DragEnter(IDataObject* dataObject, DWORD keyState, POINTL point, DWORD* effect)
+{
+ // check if data object contain drop
+ m_allowDrop = queryDataObject(dataObject);
+ if (m_allowDrop) {
+ getDropData(dataObject);
+ }
+
+ *effect = DROPEFFECT_NONE;
+
+ return S_OK;
+}
+
+HRESULT
+MSWindowsDropTarget::DragOver(DWORD keyState, POINTL point, DWORD* effect)
+{
+ *effect = DROPEFFECT_NONE;
+
+ return S_OK;
+}
+
+HRESULT
+MSWindowsDropTarget::DragLeave(void)
+{
+ return S_OK;
+}
+
+HRESULT
+MSWindowsDropTarget::Drop(IDataObject* dataObject, DWORD keyState, POINTL point, DWORD* effect)
+{
+ *effect = DROPEFFECT_NONE;
+
+ return S_OK;
+}
+
+bool
+MSWindowsDropTarget::queryDataObject(IDataObject* dataObject)
+{
+ // check if it supports CF_HDROP using a HGLOBAL
+ FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+
+ return dataObject->QueryGetData(&fmtetc) == S_OK ? true : false;
+}
+
+void
+MSWindowsDropTarget::setDraggingFilename(char* const filename)
+{
+ m_dragFilename = filename;
+}
+
+std::string
+MSWindowsDropTarget::getDraggingFilename()
+{
+ return m_dragFilename;
+}
+
+void
+MSWindowsDropTarget::clearDraggingFilename()
+{
+ m_dragFilename.clear();
+}
+
+void
+getDropData(IDataObject* dataObject)
+{
+ // construct a FORMATETC object
+ FORMATETC fmtEtc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM stgMed;
+
+ // See if the dataobject contains any DROP stored as a HGLOBAL
+ if (dataObject->QueryGetData(&fmtEtc) == S_OK) {
+ if (dataObject->GetData(&fmtEtc, &stgMed) == S_OK) {
+ // get data here
+ PVOID data = GlobalLock(stgMed.hGlobal);
+
+ // data object global handler contains:
+ // DROPFILESfilename1 filename2 two spaces as the end
+ // TODO: get multiple filenames
+ wchar_t* wcData = (wchar_t*)((LPBYTE)data + sizeof(DROPFILES));
+
+ // convert wchar to char
+ char* filename = new char[wcslen(wcData) + 1];
+ filename[wcslen(wcData)] = '\0';
+ wcstombs(filename, wcData, wcslen(wcData));
+
+ MSWindowsDropTarget::instance().setDraggingFilename(filename);
+
+ GlobalUnlock(stgMed.hGlobal);
+
+ // release the data using the COM API
+ ReleaseStgMedium(&stgMed);
+
+ delete[] filename;
+ }
+ }
+}
+
+HRESULT __stdcall
+MSWindowsDropTarget::QueryInterface (REFIID iid, void ** object)
+{
+ if (iid == IID_IDropTarget || iid == IID_IUnknown) {
+ AddRef();
+ *object = this;
+ return S_OK;
+ }
+ else {
+ *object = 0;
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG __stdcall
+MSWindowsDropTarget::AddRef(void)
+{
+ return InterlockedIncrement(&m_refCount);
+}
+
+ULONG __stdcall
+MSWindowsDropTarget::Release(void)
+{
+ LONG count = InterlockedDecrement(&m_refCount);
+
+ if (count == 0) {
+ delete this;
+ return 0;
+ }
+ else {
+ return count;
+ }
+}
diff --git a/src/lib/platform/MSWindowsDropTarget.h b/src/lib/platform/MSWindowsDropTarget.h
new file mode 100644
index 0000000..6d60845
--- /dev/null
+++ b/src/lib/platform/MSWindowsDropTarget.h
@@ -0,0 +1,59 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#include <string>
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <oleidl.h>
+
+class MSWindowsScreen;
+
+class MSWindowsDropTarget : public IDropTarget {
+public:
+ MSWindowsDropTarget();
+ ~MSWindowsDropTarget();
+
+ // IUnknown implementation
+ HRESULT __stdcall QueryInterface(REFIID iid, void** object);
+ ULONG __stdcall AddRef(void);
+ ULONG __stdcall Release(void);
+
+ // IDropTarget implementation
+ HRESULT __stdcall DragEnter(IDataObject* dataObject, DWORD keyState, POINTL point, DWORD* effect);
+ HRESULT __stdcall DragOver(DWORD keyState, POINTL point, DWORD* effect);
+ HRESULT __stdcall DragLeave(void);
+ HRESULT __stdcall Drop(IDataObject* dataObject, DWORD keyState, POINTL point, DWORD* effect);
+
+ void setDraggingFilename(char* const);
+ std::string getDraggingFilename();
+ void clearDraggingFilename();
+
+ static MSWindowsDropTarget&
+ instance();
+
+private:
+ bool queryDataObject(IDataObject* dataObject);
+
+ long m_refCount;
+ bool m_allowDrop;
+ std::string m_dragFilename;
+
+ static MSWindowsDropTarget*
+ s_instance;
+};
diff --git a/src/lib/platform/MSWindowsEventQueueBuffer.cpp b/src/lib/platform/MSWindowsEventQueueBuffer.cpp
new file mode 100644
index 0000000..f6de157
--- /dev/null
+++ b/src/lib/platform/MSWindowsEventQueueBuffer.cpp
@@ -0,0 +1,146 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/MSWindowsEventQueueBuffer.h"
+
+#include "arch/win32/ArchMiscWindows.h"
+#include "mt/Thread.h"
+#include "base/IEventQueue.h"
+
+//
+// EventQueueTimer
+//
+
+class EventQueueTimer { };
+
+
+//
+// MSWindowsEventQueueBuffer
+//
+
+MSWindowsEventQueueBuffer::MSWindowsEventQueueBuffer(IEventQueue* events) :
+ m_events(events)
+{
+ // remember thread. we'll be posting messages to it.
+ m_thread = GetCurrentThreadId();
+
+ // create a message type for custom events
+ m_userEvent = RegisterWindowMessage("BARRIER_USER_EVENT");
+
+ // get message type for daemon quit
+ m_daemonQuit = ArchMiscWindows::getDaemonQuitMessage();
+
+ // make sure this thread has a message queue
+ MSG dummy;
+ PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+}
+
+MSWindowsEventQueueBuffer::~MSWindowsEventQueueBuffer()
+{
+ // do nothing
+}
+
+void
+MSWindowsEventQueueBuffer::waitForEvent(double timeout)
+{
+ // check if messages are available first. if we don't do this then
+ // MsgWaitForMultipleObjects() will block even if the queue isn't
+ // empty if the messages in the queue were there before the last
+ // call to GetMessage()/PeekMessage().
+ if (!isEmpty()) {
+ return;
+ }
+
+ // convert timeout
+ DWORD t;
+ if (timeout < 0.0) {
+ t = INFINITE;
+ }
+ else {
+ t = (DWORD)(1000.0 * timeout);
+ }
+
+ // wait for a message. we cannot be interrupted by thread
+ // cancellation but that's okay because we're run in the main
+ // thread and we never cancel that thread.
+ HANDLE dummy[1];
+ MsgWaitForMultipleObjects(0, dummy, FALSE, t, QS_ALLINPUT);
+}
+
+IEventQueueBuffer::Type
+MSWindowsEventQueueBuffer::getEvent(Event& event, UInt32& dataID)
+{
+ // peek at messages first. waiting for QS_ALLINPUT will return
+ // if a message has been sent to our window but GetMessage will
+ // dispatch that message behind our backs and block. PeekMessage
+ // will also dispatch behind our backs but won't block.
+ if (!PeekMessage(&m_event, NULL, 0, 0, PM_NOREMOVE) &&
+ !PeekMessage(&m_event, (HWND)-1, 0, 0, PM_NOREMOVE)) {
+ return kNone;
+ }
+
+ // BOOL. yeah, right.
+ BOOL result = GetMessage(&m_event, NULL, 0, 0);
+ if (result == -1) {
+ return kNone;
+ }
+ else if (result == 0) {
+ event = Event(Event::kQuit);
+ return kSystem;
+ }
+ else if (m_daemonQuit != 0 && m_event.message == m_daemonQuit) {
+ event = Event(Event::kQuit);
+ return kSystem;
+ }
+ else if (m_event.message == m_userEvent) {
+ dataID = static_cast<UInt32>(m_event.wParam);
+ return kUser;
+ }
+ else {
+ event = Event(Event::kSystem,
+ m_events->getSystemTarget(), &m_event);
+ return kSystem;
+ }
+}
+
+bool
+MSWindowsEventQueueBuffer::addEvent(UInt32 dataID)
+{
+ return (PostThreadMessage(m_thread, m_userEvent,
+ static_cast<WPARAM>(dataID), 0) != 0);
+}
+
+bool
+MSWindowsEventQueueBuffer::isEmpty() const
+{
+ // don't use QS_POINTER, QS_TOUCH, or any meta-flags that include them (like QS_ALLINPUT)
+ // because they can cause GetQueueStatus() to always return 0 and we miss events
+ return (HIWORD(GetQueueStatus(QS_POSTMESSAGE)) == 0);
+}
+
+EventQueueTimer*
+MSWindowsEventQueueBuffer::newTimer(double, bool) const
+{
+ return new EventQueueTimer;
+}
+
+void
+MSWindowsEventQueueBuffer::deleteTimer(EventQueueTimer* timer) const
+{
+ delete timer;
+}
diff --git a/src/lib/platform/MSWindowsEventQueueBuffer.h b/src/lib/platform/MSWindowsEventQueueBuffer.h
new file mode 100644
index 0000000..6a0f9f9
--- /dev/null
+++ b/src/lib/platform/MSWindowsEventQueueBuffer.h
@@ -0,0 +1,50 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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/IEventQueueBuffer.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+class IEventQueue;
+
+//! Event queue buffer for Win32
+class MSWindowsEventQueueBuffer : public IEventQueueBuffer {
+public:
+ MSWindowsEventQueueBuffer(IEventQueue* events);
+ virtual ~MSWindowsEventQueueBuffer();
+
+ // IEventQueueBuffer overrides
+ virtual void init() { }
+ virtual void waitForEvent(double timeout);
+ virtual Type getEvent(Event& event, UInt32& dataID);
+ virtual bool addEvent(UInt32 dataID);
+ virtual bool isEmpty() const;
+ virtual EventQueueTimer*
+ newTimer(double duration, bool oneShot) const;
+ virtual void deleteTimer(EventQueueTimer*) const;
+
+private:
+ DWORD m_thread;
+ UINT m_userEvent;
+ MSG m_event;
+ UINT m_daemonQuit;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/platform/MSWindowsHook.cpp b/src/lib/platform/MSWindowsHook.cpp
new file mode 100644
index 0000000..929888e
--- /dev/null
+++ b/src/lib/platform/MSWindowsHook.cpp
@@ -0,0 +1,629 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2018 Debauchee Open Source Group
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 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 "platform/MSWindowsHook.h"
+#include "platform/MSWindowsHookResource.h"
+#include "platform/ImmuneKeysReader.h"
+#include "barrier/protocol_types.h"
+#include "barrier/XScreen.h"
+#include "base/Log.h"
+
+ //
+ // debugging compile flag. when not zero the server doesn't grab
+ // the keyboard when the mouse leaves the server screen. this
+ // makes it possible to use the debugger (via the keyboard) when
+ // all user input would normally be caught by the hook procedures.
+ //
+#define NO_GRAB_KEYBOARD 0
+
+static const DWORD g_threadID = GetCurrentThreadId();
+
+static WindowsHookResource g_hkMessage;
+static WindowsHookResource g_hkKeyboard;
+static WindowsHookResource g_hkMouse;
+static EHookMode g_mode = kHOOK_DISABLE;
+static UInt32 g_zoneSides = 0;
+static SInt32 g_zoneSize = 0;
+static SInt32 g_xScreen = 0;
+static SInt32 g_yScreen = 0;
+static SInt32 g_wScreen = 0;
+static SInt32 g_hScreen = 0;
+static WPARAM g_deadVirtKey = 0;
+static WPARAM g_deadRelease = 0;
+static LPARAM g_deadLParam = 0;
+static BYTE g_deadKeyState[256] = { 0 };
+static BYTE g_keyState[256] = { 0 };
+static bool g_fakeServerInput = false;
+static std::vector<DWORD> g_immuneKeys;
+
+static const std::string ImmuneKeysPath = ArchFileWindows().getProfileDirectory() + "\\ImmuneKeys.txt";
+
+static std::vector<DWORD> immune_keys_list()
+{
+ std::vector<DWORD> keys;
+ std::string badLine;
+ if (!ImmuneKeysReader::get_list(ImmuneKeysPath.c_str(), keys, badLine))
+ LOG((CLOG_ERR "Reading immune keys stopped at: %s", badLine.c_str()));
+ return keys;
+}
+
+inline static
+bool is_immune_key(DWORD target)
+{
+ for (auto key : g_immuneKeys) {
+ if (key == target)
+ return true;
+ }
+ return false;
+}
+
+void
+MSWindowsHook::setSides(UInt32 sides)
+{
+ g_zoneSides = sides;
+}
+
+void
+MSWindowsHook::setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize)
+{
+ g_zoneSize = jumpZoneSize;
+ g_xScreen = x;
+ g_yScreen = y;
+ g_wScreen = w;
+ g_hScreen = h;
+}
+
+void
+MSWindowsHook::setMode(EHookMode mode)
+{
+ g_mode = mode;
+}
+
+#if !NO_GRAB_KEYBOARD
+static
+void
+keyboardGetState(BYTE keys[256], DWORD vkCode, bool kf_up)
+{
+ // we have to use GetAsyncKeyState() rather than GetKeyState() because
+ // we don't pass through most keys so the event synchronous state
+ // doesn't get updated. we do that because certain modifier keys have
+ // side effects, like alt and the windows key.
+ if (vkCode < 0 || vkCode >= 256) {
+ return;
+ }
+
+ // Keep track of key state on our own in case GetAsyncKeyState() fails
+ g_keyState[vkCode] = kf_up ? 0 : 0x80;
+ g_keyState[VK_SHIFT] = g_keyState[VK_LSHIFT] | g_keyState[VK_RSHIFT];
+
+ SHORT key;
+ // Test whether GetAsyncKeyState() is being honest with us
+ key = GetAsyncKeyState(vkCode);
+
+ if (key & 0x80) {
+ // The only time we know for sure that GetAsyncKeyState() is working
+ // is when it tells us that the current key is down.
+ // In this case, update g_keyState to reflect what GetAsyncKeyState()
+ // is telling us, just in case we have gotten out of sync
+
+ for (int i = 0; i < 256; ++i) {
+ key = GetAsyncKeyState(i);
+ g_keyState[i] = (BYTE)((key < 0) ? 0x80u : 0);
+ }
+ }
+
+ // copy g_keyState to keys
+ for (int i = 0; i < 256; ++i) {
+ keys[i] = g_keyState[i];
+ }
+
+ key = GetKeyState(VK_CAPITAL);
+ keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1));
+}
+
+static
+WPARAM
+makeKeyMsg(UINT virtKey, char c, bool noAltGr)
+{
+ return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), noAltGr ? 1 : 0);
+}
+
+static
+bool
+keyboardHookHandler(WPARAM wParam, LPARAM lParam)
+{
+ DWORD vkCode = static_cast<DWORD>(wParam);
+ bool kf_up = (lParam & (KF_UP << 16)) != 0;
+
+ // check for special events indicating if we should start or stop
+ // passing events through and not report them to the server. this
+ // is used to allow the server to synthesize events locally but
+ // not pick them up as user events.
+ if (wParam == BARRIER_HOOK_FAKE_INPUT_VIRTUAL_KEY &&
+ ((lParam >> 16) & 0xffu) == BARRIER_HOOK_FAKE_INPUT_SCANCODE) {
+ // update flag
+ g_fakeServerInput = ((lParam & 0x80000000u) == 0);
+ PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
+ 0xff000000u | wParam, lParam);
+
+ // discard event
+ return true;
+ }
+
+ // if we're expecting fake input then just pass the event through
+ // and do not forward to the server
+ if (g_fakeServerInput) {
+ PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
+ 0xfe000000u | wParam, lParam);
+ return false;
+ }
+
+ // VK_RSHIFT may be sent with an extended scan code but right shift
+ // is not an extended key so we reset that bit.
+ if (wParam == VK_RSHIFT) {
+ lParam &= ~0x01000000u;
+ }
+
+ // tell server about event
+ PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG, wParam, lParam);
+
+ // ignore dead key release
+ if ((g_deadVirtKey == wParam || g_deadRelease == wParam) &&
+ (lParam & 0x80000000u) != 0) {
+ g_deadRelease = 0;
+ PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
+ wParam | 0x04000000, lParam);
+ return false;
+ }
+
+ // we need the keyboard state for ToAscii()
+ BYTE keys[256];
+ keyboardGetState(keys, vkCode, kf_up);
+
+ // ToAscii() maps ctrl+letter to the corresponding control code
+ // and ctrl+backspace to delete. we don't want those translations
+ // so clear the control modifier state. however, if we want to
+ // simulate AltGr (which is ctrl+alt) then we must not clear it.
+ UINT control = keys[VK_CONTROL] | keys[VK_LCONTROL] | keys[VK_RCONTROL];
+ UINT menu = keys[VK_MENU] | keys[VK_LMENU] | keys[VK_RMENU];
+ if ((control & 0x80) == 0 || (menu & 0x80) == 0) {
+ keys[VK_LCONTROL] = 0;
+ keys[VK_RCONTROL] = 0;
+ keys[VK_CONTROL] = 0;
+ } else {
+ keys[VK_LCONTROL] = 0x80;
+ keys[VK_RCONTROL] = 0x80;
+ keys[VK_CONTROL] = 0x80;
+ keys[VK_LMENU] = 0x80;
+ keys[VK_RMENU] = 0x80;
+ keys[VK_MENU] = 0x80;
+ }
+
+ // ToAscii() needs to know if a menu is active for some reason.
+ // we don't know and there doesn't appear to be any way to find
+ // out. so we'll just assume a menu is active if the menu key
+ // is down.
+ // FIXME -- figure out some way to check if a menu is active
+ UINT flags = 0;
+ if ((menu & 0x80) != 0)
+ flags |= 1;
+
+ // if we're on the server screen then just pass numpad keys with alt
+ // key down as-is. we won't pick up the resulting character but the
+ // local app will. if on a client screen then grab keys as usual;
+ // if the client is a windows system it'll synthesize the expected
+ // character. if not then it'll probably just do nothing.
+ if (g_mode != kHOOK_RELAY_EVENTS) {
+ // we don't use virtual keys because we don't know what the
+ // state of the numlock key is. we'll hard code the scan codes
+ // instead. hopefully this works across all keyboards.
+ UINT sc = (lParam & 0x01ff0000u) >> 16;
+ if (menu &&
+ (sc >= 0x47u && sc <= 0x52u && sc != 0x4au && sc != 0x4eu)) {
+ return false;
+ }
+ }
+
+ WORD c = 0;
+
+ // map the key event to a character. we have to put the dead
+ // key back first and this has the side effect of removing it.
+ if (g_deadVirtKey != 0) {
+ if (ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
+ g_deadKeyState, &c, flags) == 2) {
+ // If ToAscii returned 2, it means that we accidentally removed
+ // a double dead key instead of restoring it. Thus, we call
+ // ToAscii again with the same parameters to restore the
+ // internal dead key state.
+ ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
+ g_deadKeyState, &c, flags);
+
+ // We need to keep track of this because g_deadVirtKey will be
+ // cleared later on; this would cause the dead key release to
+ // incorrectly restore the dead key state.
+ g_deadRelease = g_deadVirtKey;
+ }
+ }
+
+ UINT scanCode = ((lParam & 0x10ff0000u) >> 16);
+ int n = ToAscii((UINT)wParam, scanCode, keys, &c, flags);
+
+ // if mapping failed and ctrl and alt are pressed then try again
+ // with both not pressed. this handles the case where ctrl and
+ // alt are being used as individual modifiers rather than AltGr.
+ // we note that's the case in the message sent back to barrier
+ // because there's no simple way to deduce it after the fact.
+ // we have to put the dead key back first, if there was one.
+ bool noAltGr = false;
+ if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) {
+ noAltGr = true;
+ PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
+ wParam | 0x05000000, lParam);
+ if (g_deadVirtKey != 0) {
+ if (ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
+ g_deadKeyState, &c, flags) == 2) {
+ ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
+ g_deadKeyState, &c, flags);
+ g_deadRelease = g_deadVirtKey;
+ }
+ }
+ BYTE keys2[256];
+ for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) {
+ keys2[i] = keys[i];
+ }
+ keys2[VK_LCONTROL] = 0;
+ keys2[VK_RCONTROL] = 0;
+ keys2[VK_CONTROL] = 0;
+ keys2[VK_LMENU] = 0;
+ keys2[VK_RMENU] = 0;
+ keys2[VK_MENU] = 0;
+ n = ToAscii((UINT)wParam, scanCode, keys2, &c, flags);
+ }
+
+ PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
+ wParam | ((c & 0xff) << 8) |
+ ((n & 0xff) << 16) | 0x06000000,
+ lParam);
+ WPARAM charAndVirtKey = 0;
+ bool clearDeadKey = false;
+ switch (n) {
+ default:
+ // key is a dead key
+
+ if (lParam & 0x80000000u)
+ // This handles the obscure situation where a key has been
+ // pressed which is both a dead key and a normal character
+ // depending on which modifiers have been pressed. We
+ // break here to prevent it from being considered a dead
+ // key.
+ break;
+
+ g_deadVirtKey = wParam;
+ g_deadLParam = lParam;
+ for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) {
+ g_deadKeyState[i] = keys[i];
+ }
+ break;
+
+ case 0:
+ // key doesn't map to a character. this can happen if
+ // non-character keys are pressed after a dead key.
+ charAndVirtKey = makeKeyMsg((UINT)wParam, (char)0, noAltGr);
+ break;
+
+ case 1:
+ // key maps to a character composed with dead key
+ charAndVirtKey = makeKeyMsg((UINT)wParam, (char)LOBYTE(c), noAltGr);
+ clearDeadKey = true;
+ break;
+
+ case 2: {
+ // previous dead key not composed. send a fake key press
+ // and release for the dead key to our window.
+ WPARAM deadCharAndVirtKey =
+ makeKeyMsg((UINT)g_deadVirtKey, (char)LOBYTE(c), noAltGr);
+ PostThreadMessage(g_threadID, BARRIER_MSG_KEY,
+ deadCharAndVirtKey, g_deadLParam & 0x7fffffffu);
+ PostThreadMessage(g_threadID, BARRIER_MSG_KEY,
+ deadCharAndVirtKey, g_deadLParam | 0x80000000u);
+
+ // use uncomposed character
+ charAndVirtKey = makeKeyMsg((UINT)wParam, (char)HIBYTE(c), noAltGr);
+ clearDeadKey = true;
+ break;
+ }
+ }
+
+ // put back the dead key, if any, for the application to use
+ if (g_deadVirtKey != 0) {
+ ToAscii((UINT)g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
+ g_deadKeyState, &c, flags);
+ }
+
+ // clear out old dead key state
+ if (clearDeadKey) {
+ g_deadVirtKey = 0;
+ g_deadLParam = 0;
+ }
+
+ // forward message to our window. do this whether or not we're
+ // forwarding events to clients because this'll keep our thread's
+ // key state table up to date. that's important for querying
+ // the scroll lock toggle state.
+ // XXX -- with hot keys for actions we may only need to do this when
+ // forwarding.
+ if (charAndVirtKey != 0) {
+ PostThreadMessage(g_threadID, BARRIER_MSG_DEBUG,
+ charAndVirtKey | 0x07000000, lParam);
+ PostThreadMessage(g_threadID, BARRIER_MSG_KEY, charAndVirtKey, lParam);
+ }
+
+ if (g_mode == kHOOK_RELAY_EVENTS) {
+ // let certain keys pass through
+ switch (wParam) {
+ case VK_CAPITAL:
+ case VK_NUMLOCK:
+ case VK_SCROLL:
+ // pass event on. we want to let these through to
+ // the window proc because otherwise the keyboard
+ // lights may not stay synchronized.
+ case VK_HANGUL:
+ // pass event on because we're using a low level hook
+
+ break;
+
+ default:
+ // discard
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static
+LRESULT CALLBACK
+keyboardLLHook(int code, WPARAM wParam, LPARAM lParam)
+{
+ // decode the message
+ KBDLLHOOKSTRUCT* info = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
+
+ // do not filter non-action events nor immune keys
+ if (code == HC_ACTION && !is_immune_key(info->vkCode)) {
+ WPARAM wParam = info->vkCode;
+ LPARAM lParam = 1; // repeat code
+ lParam |= (info->scanCode << 16); // scan code
+ if (info->flags & LLKHF_EXTENDED) {
+ lParam |= (1lu << 24); // extended key
+ }
+ if (info->flags & LLKHF_ALTDOWN) {
+ lParam |= (1lu << 29); // context code
+ }
+ if (info->flags & LLKHF_UP) {
+ lParam |= (1lu << 31); // transition
+ }
+ // FIXME -- bit 30 should be set if key was already down but
+ // we don't know that info. as a result we'll never generate
+ // key repeat events.
+
+ // handle the message
+ if (keyboardHookHandler(wParam, lParam)) {
+ return 1;
+ }
+ }
+
+ return CallNextHookEx(g_hkKeyboard, code, wParam, lParam);
+}
+#endif // !NO_GRAB_KEYBOARD
+
+static
+bool
+mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data)
+{
+ switch (wParam) {
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_XBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ case WM_XBUTTONDBLCLK:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_XBUTTONUP:
+ case WM_NCLBUTTONDOWN:
+ case WM_NCMBUTTONDOWN:
+ case WM_NCRBUTTONDOWN:
+ case WM_NCXBUTTONDOWN:
+ case WM_NCLBUTTONDBLCLK:
+ case WM_NCMBUTTONDBLCLK:
+ case WM_NCRBUTTONDBLCLK:
+ case WM_NCXBUTTONDBLCLK:
+ case WM_NCLBUTTONUP:
+ case WM_NCMBUTTONUP:
+ case WM_NCRBUTTONUP:
+ case WM_NCXBUTTONUP:
+ // always relay the event. eat it if relaying.
+ PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_BUTTON, wParam, data);
+ return (g_mode == kHOOK_RELAY_EVENTS);
+
+ case WM_MOUSEWHEEL:
+ if (g_mode == kHOOK_RELAY_EVENTS) {
+ // relay event
+ PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_WHEEL, data, 0);
+ }
+ return (g_mode == kHOOK_RELAY_EVENTS);
+
+ case WM_NCMOUSEMOVE:
+ case WM_MOUSEMOVE:
+ if (g_mode == kHOOK_RELAY_EVENTS) {
+ // relay and eat event
+ PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_MOVE, x, y);
+ return true;
+ } else if (g_mode == kHOOK_WATCH_JUMP_ZONE) {
+ // low level hooks can report bogus mouse positions that are
+ // outside of the screen. jeez. naturally we end up getting
+ // fake motion in the other direction to get the position back
+ // on the screen, which plays havoc with switch on double tap.
+ // Server deals with that. we'll clamp positions onto the
+ // screen. also, if we discard events for positions outside
+ // of the screen then the mouse appears to get a bit jerky
+ // near the edge. we can either accept that or pass the bogus
+ // events. we'll try passing the events.
+ bool bogus = false;
+ if (x < g_xScreen) {
+ x = g_xScreen;
+ bogus = true;
+ } else if (x >= g_xScreen + g_wScreen) {
+ x = g_xScreen + g_wScreen - 1;
+ bogus = true;
+ }
+ if (y < g_yScreen) {
+ y = g_yScreen;
+ bogus = true;
+ } else if (y >= g_yScreen + g_hScreen) {
+ y = g_yScreen + g_hScreen - 1;
+ bogus = true;
+ }
+
+ // check for mouse inside jump zone
+ bool inside = false;
+ if (!inside && (g_zoneSides & kLeftMask) != 0) {
+ inside = (x < g_xScreen + g_zoneSize);
+ }
+ if (!inside && (g_zoneSides & kRightMask) != 0) {
+ inside = (x >= g_xScreen + g_wScreen - g_zoneSize);
+ }
+ if (!inside && (g_zoneSides & kTopMask) != 0) {
+ inside = (y < g_yScreen + g_zoneSize);
+ }
+ if (!inside && (g_zoneSides & kBottomMask) != 0) {
+ inside = (y >= g_yScreen + g_hScreen - g_zoneSize);
+ }
+
+ // relay the event
+ PostThreadMessage(g_threadID, BARRIER_MSG_MOUSE_MOVE, x, y);
+
+ // if inside and not bogus then eat the event
+ return inside && !bogus;
+ }
+ }
+
+ // pass the event
+ return false;
+}
+
+static
+LRESULT CALLBACK
+mouseLLHook(int code, WPARAM wParam, LPARAM lParam)
+{
+ // do not filter non-action events
+ if (code == HC_ACTION) {
+ // decode the message
+ MSLLHOOKSTRUCT* info = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
+ SInt32 x = static_cast<SInt32>(info->pt.x);
+ SInt32 y = static_cast<SInt32>(info->pt.y);
+ SInt32 w = static_cast<SInt16>(HIWORD(info->mouseData));
+
+ // handle the message
+ if (mouseHookHandler(wParam, x, y, w)) {
+ return 1;
+ }
+ }
+
+ return CallNextHookEx(g_hkMouse, code, wParam, lParam);
+}
+
+bool
+MSWindowsHook::install()
+{
+ // discard old dead keys
+ g_deadVirtKey = 0;
+ g_deadLParam = 0;
+
+ // reset fake input flag
+ g_fakeServerInput = false;
+
+ // setup immune keys
+ g_immuneKeys = immune_keys_list();
+ LOG((CLOG_DEBUG "Found %u immune keys in %s", g_immuneKeys.size(), ImmuneKeysPath.c_str()));
+
+#if NO_GRAB_KEYBOARD
+ // we only need the mouse hook
+ if (!g_hkMouse.set(WH_MOUSE_LL, &mouseLLHook, NULL, 0))
+ return false;
+#else
+ // we need both hooks. if either fails, discard the other
+ if (!g_hkMouse.set(WH_MOUSE_LL, &mouseLLHook, NULL, 0) ||
+ !g_hkKeyboard.set(WH_KEYBOARD_LL, &keyboardLLHook, NULL, 0)) {
+ g_hkMouse.unset();
+ g_hkKeyboard.unset();
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+void
+MSWindowsHook::uninstall()
+{
+ // discard old dead keys
+ g_deadVirtKey = 0;
+ g_deadLParam = 0;
+
+ g_hkMouse.unset();
+ g_hkKeyboard.unset();
+
+ uninstallScreenSaver();
+}
+
+static
+LRESULT CALLBACK
+getMessageHook(int code, WPARAM wParam, LPARAM lParam)
+{
+ if (code >= 0) {
+ MSG* msg = reinterpret_cast<MSG*>(lParam);
+ if (msg->message == WM_SYSCOMMAND &&
+ msg->wParam == SC_SCREENSAVE) {
+ // broadcast screen saver started message
+ PostThreadMessage(g_threadID,
+ BARRIER_MSG_SCREEN_SAVER, TRUE, 0);
+ }
+ }
+
+ return CallNextHookEx(g_hkMessage, code, wParam, lParam);
+}
+
+bool
+MSWindowsHook::installScreenSaver()
+{
+ // install hook unless it's already installed
+ if (g_hkMessage.is_set())
+ return true;
+ return g_hkMessage.set(WH_GETMESSAGE, &getMessageHook, NULL, 0);
+}
+
+void
+MSWindowsHook::uninstallScreenSaver()
+{
+ g_hkMessage.unset();
+} \ No newline at end of file
diff --git a/src/lib/platform/MSWindowsHook.h b/src/lib/platform/MSWindowsHook.h
new file mode 100644
index 0000000..7b2121c
--- /dev/null
+++ b/src/lib/platform/MSWindowsHook.h
@@ -0,0 +1,39 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2018 Debauchee Open Source Group
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 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 "platform/synwinhk.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+//! Loads and provides functions for the Windows hook
+class MSWindowsHook
+{
+public:
+ void setSides(UInt32 sides);
+ void setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize);
+ void setMode(EHookMode mode);
+
+ static bool install();
+ static void uninstall();
+ static bool installScreenSaver();
+ static void uninstallScreenSaver();
+};
diff --git a/src/lib/platform/MSWindowsHookResource.cpp b/src/lib/platform/MSWindowsHookResource.cpp
new file mode 100644
index 0000000..ced5ff1
--- /dev/null
+++ b/src/lib/platform/MSWindowsHookResource.cpp
@@ -0,0 +1,33 @@
+#include "MSWindowsHookResource.h"
+
+WindowsHookResource::WindowsHookResource() :
+ _hook(NULL)
+{
+}
+
+WindowsHookResource::~WindowsHookResource()
+{
+ unset();
+}
+
+bool WindowsHookResource::set(int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId)
+{
+ if (is_set())
+ return false;
+ _hook = SetWindowsHookEx(idHook, lpfn, hmod, dwThreadId);
+ return is_set();
+}
+
+bool WindowsHookResource::unset()
+{
+ if (is_set()) {
+ if (UnhookWindowsHookEx(_hook) == 0) {
+ return false;
+ }
+ _hook = NULL;
+ }
+ return true;
+}
+
+bool WindowsHookResource::is_set() const { return _hook != NULL; }
+WindowsHookResource::operator HHOOK() const { return _hook; } \ No newline at end of file
diff --git a/src/lib/platform/MSWindowsHookResource.h b/src/lib/platform/MSWindowsHookResource.h
new file mode 100644
index 0000000..b66c4b8
--- /dev/null
+++ b/src/lib/platform/MSWindowsHookResource.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+class WindowsHookResource
+{
+public:
+ explicit WindowsHookResource();
+ ~WindowsHookResource();
+
+ bool set(int idHook, HOOKPROC lpfn, HINSTANCE hmod, DWORD dwThreadId);
+ bool unset();
+
+ bool is_set() const;
+ operator HHOOK() const;
+
+private:
+ HHOOK _hook;
+}; \ No newline at end of file
diff --git a/src/lib/platform/MSWindowsKeyState.cpp b/src/lib/platform/MSWindowsKeyState.cpp
new file mode 100644
index 0000000..2f29f72
--- /dev/null
+++ b/src/lib/platform/MSWindowsKeyState.cpp
@@ -0,0 +1,1406 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "platform/MSWindowsKeyState.h"
+
+#include "platform/MSWindowsDesks.h"
+#include "mt/Thread.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "base/FunctionJob.h"
+#include "base/Log.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+
+// extended mouse buttons
+#if !defined(VK_XBUTTON1)
+#define VK_XBUTTON1 0x05
+#define VK_XBUTTON2 0x06
+#endif
+
+//
+// MSWindowsKeyState
+//
+
+// map virtual keys to barrier key enumeration
+const KeyID MSWindowsKeyState::s_virtualKey[] =
+{
+ /* 0x000 */ { kKeyNone }, // reserved
+ /* 0x001 */ { kKeyNone }, // VK_LBUTTON
+ /* 0x002 */ { kKeyNone }, // VK_RBUTTON
+ /* 0x003 */ { kKeyNone }, // VK_CANCEL
+ /* 0x004 */ { kKeyNone }, // VK_MBUTTON
+ /* 0x005 */ { kKeyNone }, // VK_XBUTTON1
+ /* 0x006 */ { kKeyNone }, // VK_XBUTTON2
+ /* 0x007 */ { kKeyNone }, // undefined
+ /* 0x008 */ { kKeyBackSpace }, // VK_BACK
+ /* 0x009 */ { kKeyTab }, // VK_TAB
+ /* 0x00a */ { kKeyNone }, // undefined
+ /* 0x00b */ { kKeyNone }, // undefined
+ /* 0x00c */ { kKeyClear }, // VK_CLEAR
+ /* 0x00d */ { kKeyReturn }, // VK_RETURN
+ /* 0x00e */ { kKeyNone }, // undefined
+ /* 0x00f */ { kKeyNone }, // undefined
+ /* 0x010 */ { kKeyShift_L }, // VK_SHIFT
+ /* 0x011 */ { kKeyControl_L }, // VK_CONTROL
+ /* 0x012 */ { kKeyAlt_L }, // VK_MENU
+ /* 0x013 */ { kKeyPause }, // VK_PAUSE
+ /* 0x014 */ { kKeyCapsLock }, // VK_CAPITAL
+ /* 0x015 */ { kKeyKana }, // VK_HANGUL, VK_KANA
+ /* 0x016 */ { kKeyNone }, // undefined
+ /* 0x017 */ { kKeyNone }, // VK_JUNJA
+ /* 0x018 */ { kKeyNone }, // VK_FINAL
+ /* 0x019 */ { kKeyKanzi }, // VK_HANJA, VK_KANJI
+ /* 0x01a */ { kKeyNone }, // undefined
+ /* 0x01b */ { kKeyEscape }, // VK_ESCAPE
+ /* 0x01c */ { kKeyHenkan }, // VK_CONVERT
+ /* 0x01d */ { kKeyNone }, // VK_NONCONVERT
+ /* 0x01e */ { kKeyNone }, // VK_ACCEPT
+ /* 0x01f */ { kKeyNone }, // VK_MODECHANGE
+ /* 0x020 */ { kKeyNone }, // VK_SPACE
+ /* 0x021 */ { kKeyKP_PageUp }, // VK_PRIOR
+ /* 0x022 */ { kKeyKP_PageDown },// VK_NEXT
+ /* 0x023 */ { kKeyKP_End }, // VK_END
+ /* 0x024 */ { kKeyKP_Home }, // VK_HOME
+ /* 0x025 */ { kKeyKP_Left }, // VK_LEFT
+ /* 0x026 */ { kKeyKP_Up }, // VK_UP
+ /* 0x027 */ { kKeyKP_Right }, // VK_RIGHT
+ /* 0x028 */ { kKeyKP_Down }, // VK_DOWN
+ /* 0x029 */ { kKeySelect }, // VK_SELECT
+ /* 0x02a */ { kKeyNone }, // VK_PRINT
+ /* 0x02b */ { kKeyExecute }, // VK_EXECUTE
+ /* 0x02c */ { kKeyPrint }, // VK_SNAPSHOT
+ /* 0x02d */ { kKeyKP_Insert }, // VK_INSERT
+ /* 0x02e */ { kKeyKP_Delete }, // VK_DELETE
+ /* 0x02f */ { kKeyHelp }, // VK_HELP
+ /* 0x030 */ { kKeyNone }, // VK_0
+ /* 0x031 */ { kKeyNone }, // VK_1
+ /* 0x032 */ { kKeyNone }, // VK_2
+ /* 0x033 */ { kKeyNone }, // VK_3
+ /* 0x034 */ { kKeyNone }, // VK_4
+ /* 0x035 */ { kKeyNone }, // VK_5
+ /* 0x036 */ { kKeyNone }, // VK_6
+ /* 0x037 */ { kKeyNone }, // VK_7
+ /* 0x038 */ { kKeyNone }, // VK_8
+ /* 0x039 */ { kKeyNone }, // VK_9
+ /* 0x03a */ { kKeyNone }, // undefined
+ /* 0x03b */ { kKeyNone }, // undefined
+ /* 0x03c */ { kKeyNone }, // undefined
+ /* 0x03d */ { kKeyNone }, // undefined
+ /* 0x03e */ { kKeyNone }, // undefined
+ /* 0x03f */ { kKeyNone }, // undefined
+ /* 0x040 */ { kKeyNone }, // undefined
+ /* 0x041 */ { kKeyNone }, // VK_A
+ /* 0x042 */ { kKeyNone }, // VK_B
+ /* 0x043 */ { kKeyNone }, // VK_C
+ /* 0x044 */ { kKeyNone }, // VK_D
+ /* 0x045 */ { kKeyNone }, // VK_E
+ /* 0x046 */ { kKeyNone }, // VK_F
+ /* 0x047 */ { kKeyNone }, // VK_G
+ /* 0x048 */ { kKeyNone }, // VK_H
+ /* 0x049 */ { kKeyNone }, // VK_I
+ /* 0x04a */ { kKeyNone }, // VK_J
+ /* 0x04b */ { kKeyNone }, // VK_K
+ /* 0x04c */ { kKeyNone }, // VK_L
+ /* 0x04d */ { kKeyNone }, // VK_M
+ /* 0x04e */ { kKeyNone }, // VK_N
+ /* 0x04f */ { kKeyNone }, // VK_O
+ /* 0x050 */ { kKeyNone }, // VK_P
+ /* 0x051 */ { kKeyNone }, // VK_Q
+ /* 0x052 */ { kKeyNone }, // VK_R
+ /* 0x053 */ { kKeyNone }, // VK_S
+ /* 0x054 */ { kKeyNone }, // VK_T
+ /* 0x055 */ { kKeyNone }, // VK_U
+ /* 0x056 */ { kKeyNone }, // VK_V
+ /* 0x057 */ { kKeyNone }, // VK_W
+ /* 0x058 */ { kKeyNone }, // VK_X
+ /* 0x059 */ { kKeyNone }, // VK_Y
+ /* 0x05a */ { kKeyNone }, // VK_Z
+ /* 0x05b */ { kKeySuper_L }, // VK_LWIN
+ /* 0x05c */ { kKeySuper_R }, // VK_RWIN
+ /* 0x05d */ { kKeyMenu }, // VK_APPS
+ /* 0x05e */ { kKeyNone }, // undefined
+ /* 0x05f */ { kKeySleep }, // VK_SLEEP
+ /* 0x060 */ { kKeyKP_0 }, // VK_NUMPAD0
+ /* 0x061 */ { kKeyKP_1 }, // VK_NUMPAD1
+ /* 0x062 */ { kKeyKP_2 }, // VK_NUMPAD2
+ /* 0x063 */ { kKeyKP_3 }, // VK_NUMPAD3
+ /* 0x064 */ { kKeyKP_4 }, // VK_NUMPAD4
+ /* 0x065 */ { kKeyKP_5 }, // VK_NUMPAD5
+ /* 0x066 */ { kKeyKP_6 }, // VK_NUMPAD6
+ /* 0x067 */ { kKeyKP_7 }, // VK_NUMPAD7
+ /* 0x068 */ { kKeyKP_8 }, // VK_NUMPAD8
+ /* 0x069 */ { kKeyKP_9 }, // VK_NUMPAD9
+ /* 0x06a */ { kKeyKP_Multiply },// VK_MULTIPLY
+ /* 0x06b */ { kKeyKP_Add }, // VK_ADD
+ /* 0x06c */ { kKeyKP_Separator },// VK_SEPARATOR
+ /* 0x06d */ { kKeyKP_Subtract },// VK_SUBTRACT
+ /* 0x06e */ { kKeyKP_Decimal }, // VK_DECIMAL
+ /* 0x06f */ { kKeyNone }, // VK_DIVIDE
+ /* 0x070 */ { kKeyF1 }, // VK_F1
+ /* 0x071 */ { kKeyF2 }, // VK_F2
+ /* 0x072 */ { kKeyF3 }, // VK_F3
+ /* 0x073 */ { kKeyF4 }, // VK_F4
+ /* 0x074 */ { kKeyF5 }, // VK_F5
+ /* 0x075 */ { kKeyF6 }, // VK_F6
+ /* 0x076 */ { kKeyF7 }, // VK_F7
+ /* 0x077 */ { kKeyF8 }, // VK_F8
+ /* 0x078 */ { kKeyF9 }, // VK_F9
+ /* 0x079 */ { kKeyF10 }, // VK_F10
+ /* 0x07a */ { kKeyF11 }, // VK_F11
+ /* 0x07b */ { kKeyF12 }, // VK_F12
+ /* 0x07c */ { kKeyF13 }, // VK_F13
+ /* 0x07d */ { kKeyF14 }, // VK_F14
+ /* 0x07e */ { kKeyF15 }, // VK_F15
+ /* 0x07f */ { kKeyF16 }, // VK_F16
+ /* 0x080 */ { kKeyF17 }, // VK_F17
+ /* 0x081 */ { kKeyF18 }, // VK_F18
+ /* 0x082 */ { kKeyF19 }, // VK_F19
+ /* 0x083 */ { kKeyF20 }, // VK_F20
+ /* 0x084 */ { kKeyF21 }, // VK_F21
+ /* 0x085 */ { kKeyF22 }, // VK_F22
+ /* 0x086 */ { kKeyF23 }, // VK_F23
+ /* 0x087 */ { kKeyF24 }, // VK_F24
+ /* 0x088 */ { kKeyNone }, // unassigned
+ /* 0x089 */ { kKeyNone }, // unassigned
+ /* 0x08a */ { kKeyNone }, // unassigned
+ /* 0x08b */ { kKeyNone }, // unassigned
+ /* 0x08c */ { kKeyNone }, // unassigned
+ /* 0x08d */ { kKeyNone }, // unassigned
+ /* 0x08e */ { kKeyNone }, // unassigned
+ /* 0x08f */ { kKeyNone }, // unassigned
+ /* 0x090 */ { kKeyNumLock }, // VK_NUMLOCK
+ /* 0x091 */ { kKeyScrollLock }, // VK_SCROLL
+ /* 0x092 */ { kKeyNone }, // unassigned
+ /* 0x093 */ { kKeyNone }, // unassigned
+ /* 0x094 */ { kKeyNone }, // unassigned
+ /* 0x095 */ { kKeyNone }, // unassigned
+ /* 0x096 */ { kKeyNone }, // unassigned
+ /* 0x097 */ { kKeyNone }, // unassigned
+ /* 0x098 */ { kKeyNone }, // unassigned
+ /* 0x099 */ { kKeyNone }, // unassigned
+ /* 0x09a */ { kKeyNone }, // unassigned
+ /* 0x09b */ { kKeyNone }, // unassigned
+ /* 0x09c */ { kKeyNone }, // unassigned
+ /* 0x09d */ { kKeyNone }, // unassigned
+ /* 0x09e */ { kKeyNone }, // unassigned
+ /* 0x09f */ { kKeyNone }, // unassigned
+ /* 0x0a0 */ { kKeyShift_L }, // VK_LSHIFT
+ /* 0x0a1 */ { kKeyShift_R }, // VK_RSHIFT
+ /* 0x0a2 */ { kKeyControl_L }, // VK_LCONTROL
+ /* 0x0a3 */ { kKeyControl_R }, // VK_RCONTROL
+ /* 0x0a4 */ { kKeyAlt_L }, // VK_LMENU
+ /* 0x0a5 */ { kKeyAlt_R }, // VK_RMENU
+ /* 0x0a6 */ { kKeyNone }, // VK_BROWSER_BACK
+ /* 0x0a7 */ { kKeyNone }, // VK_BROWSER_FORWARD
+ /* 0x0a8 */ { kKeyNone }, // VK_BROWSER_REFRESH
+ /* 0x0a9 */ { kKeyNone }, // VK_BROWSER_STOP
+ /* 0x0aa */ { kKeyNone }, // VK_BROWSER_SEARCH
+ /* 0x0ab */ { kKeyNone }, // VK_BROWSER_FAVORITES
+ /* 0x0ac */ { kKeyNone }, // VK_BROWSER_HOME
+ /* 0x0ad */ { kKeyNone }, // VK_VOLUME_MUTE
+ /* 0x0ae */ { kKeyNone }, // VK_VOLUME_DOWN
+ /* 0x0af */ { kKeyNone }, // VK_VOLUME_UP
+ /* 0x0b0 */ { kKeyNone }, // VK_MEDIA_NEXT_TRACK
+ /* 0x0b1 */ { kKeyNone }, // VK_MEDIA_PREV_TRACK
+ /* 0x0b2 */ { kKeyNone }, // VK_MEDIA_STOP
+ /* 0x0b3 */ { kKeyNone }, // VK_MEDIA_PLAY_PAUSE
+ /* 0x0b4 */ { kKeyNone }, // VK_LAUNCH_MAIL
+ /* 0x0b5 */ { kKeyNone }, // VK_LAUNCH_MEDIA_SELECT
+ /* 0x0b6 */ { kKeyNone }, // VK_LAUNCH_APP1
+ /* 0x0b7 */ { kKeyNone }, // VK_LAUNCH_APP2
+ /* 0x0b8 */ { kKeyNone }, // unassigned
+ /* 0x0b9 */ { kKeyNone }, // unassigned
+ /* 0x0ba */ { kKeyNone }, // OEM specific
+ /* 0x0bb */ { kKeyNone }, // OEM specific
+ /* 0x0bc */ { kKeyNone }, // OEM specific
+ /* 0x0bd */ { kKeyNone }, // OEM specific
+ /* 0x0be */ { kKeyNone }, // OEM specific
+ /* 0x0bf */ { kKeyNone }, // OEM specific
+ /* 0x0c0 */ { kKeyNone }, // OEM specific
+ /* 0x0c1 */ { kKeyNone }, // unassigned
+ /* 0x0c2 */ { kKeyNone }, // unassigned
+ /* 0x0c3 */ { kKeyNone }, // unassigned
+ /* 0x0c4 */ { kKeyNone }, // unassigned
+ /* 0x0c5 */ { kKeyNone }, // unassigned
+ /* 0x0c6 */ { kKeyNone }, // unassigned
+ /* 0x0c7 */ { kKeyNone }, // unassigned
+ /* 0x0c8 */ { kKeyNone }, // unassigned
+ /* 0x0c9 */ { kKeyNone }, // unassigned
+ /* 0x0ca */ { kKeyNone }, // unassigned
+ /* 0x0cb */ { kKeyNone }, // unassigned
+ /* 0x0cc */ { kKeyNone }, // unassigned
+ /* 0x0cd */ { kKeyNone }, // unassigned
+ /* 0x0ce */ { kKeyNone }, // unassigned
+ /* 0x0cf */ { kKeyNone }, // unassigned
+ /* 0x0d0 */ { kKeyNone }, // unassigned
+ /* 0x0d1 */ { kKeyNone }, // unassigned
+ /* 0x0d2 */ { kKeyNone }, // unassigned
+ /* 0x0d3 */ { kKeyNone }, // unassigned
+ /* 0x0d4 */ { kKeyNone }, // unassigned
+ /* 0x0d5 */ { kKeyNone }, // unassigned
+ /* 0x0d6 */ { kKeyNone }, // unassigned
+ /* 0x0d7 */ { kKeyNone }, // unassigned
+ /* 0x0d8 */ { kKeyNone }, // unassigned
+ /* 0x0d9 */ { kKeyNone }, // unassigned
+ /* 0x0da */ { kKeyNone }, // unassigned
+ /* 0x0db */ { kKeyNone }, // OEM specific
+ /* 0x0dc */ { kKeyNone }, // OEM specific
+ /* 0x0dd */ { kKeyNone }, // OEM specific
+ /* 0x0de */ { kKeyNone }, // OEM specific
+ /* 0x0df */ { kKeyNone }, // OEM specific
+ /* 0x0e0 */ { kKeyNone }, // OEM specific
+ /* 0x0e1 */ { kKeyNone }, // OEM specific
+ /* 0x0e2 */ { kKeyNone }, // OEM specific
+ /* 0x0e3 */ { kKeyNone }, // OEM specific
+ /* 0x0e4 */ { kKeyNone }, // OEM specific
+ /* 0x0e5 */ { kKeyNone }, // unassigned
+ /* 0x0e6 */ { kKeyNone }, // OEM specific
+ /* 0x0e7 */ { kKeyNone }, // unassigned
+ /* 0x0e8 */ { kKeyNone }, // unassigned
+ /* 0x0e9 */ { kKeyNone }, // OEM specific
+ /* 0x0ea */ { kKeyNone }, // OEM specific
+ /* 0x0eb */ { kKeyNone }, // OEM specific
+ /* 0x0ec */ { kKeyNone }, // OEM specific
+ /* 0x0ed */ { kKeyNone }, // OEM specific
+ /* 0x0ee */ { kKeyNone }, // OEM specific
+ /* 0x0ef */ { kKeyNone }, // OEM specific
+ /* 0x0f0 */ { kKeyNone }, // OEM specific
+ /* 0x0f1 */ { kKeyNone }, // OEM specific
+ /* 0x0f2 */ { kKeyHiraganaKatakana }, // VK_OEM_COPY
+ /* 0x0f3 */ { kKeyZenkaku }, // VK_OEM_AUTO
+ /* 0x0f4 */ { kKeyZenkaku }, // VK_OEM_ENLW
+ /* 0x0f5 */ { kKeyNone }, // OEM specific
+ /* 0x0f6 */ { kKeyNone }, // VK_ATTN
+ /* 0x0f7 */ { kKeyNone }, // VK_CRSEL
+ /* 0x0f8 */ { kKeyNone }, // VK_EXSEL
+ /* 0x0f9 */ { kKeyNone }, // VK_EREOF
+ /* 0x0fa */ { kKeyNone }, // VK_PLAY
+ /* 0x0fb */ { kKeyNone }, // VK_ZOOM
+ /* 0x0fc */ { kKeyNone }, // reserved
+ /* 0x0fd */ { kKeyNone }, // VK_PA1
+ /* 0x0fe */ { kKeyNone }, // VK_OEM_CLEAR
+ /* 0x0ff */ { kKeyNone }, // reserved
+
+ /* 0x100 */ { kKeyNone }, // reserved
+ /* 0x101 */ { kKeyNone }, // VK_LBUTTON
+ /* 0x102 */ { kKeyNone }, // VK_RBUTTON
+ /* 0x103 */ { kKeyBreak }, // VK_CANCEL
+ /* 0x104 */ { kKeyNone }, // VK_MBUTTON
+ /* 0x105 */ { kKeyNone }, // VK_XBUTTON1
+ /* 0x106 */ { kKeyNone }, // VK_XBUTTON2
+ /* 0x107 */ { kKeyNone }, // undefined
+ /* 0x108 */ { kKeyNone }, // VK_BACK
+ /* 0x109 */ { kKeyNone }, // VK_TAB
+ /* 0x10a */ { kKeyNone }, // undefined
+ /* 0x10b */ { kKeyNone }, // undefined
+ /* 0x10c */ { kKeyClear }, // VK_CLEAR
+ /* 0x10d */ { kKeyKP_Enter }, // VK_RETURN
+ /* 0x10e */ { kKeyNone }, // undefined
+ /* 0x10f */ { kKeyNone }, // undefined
+ /* 0x110 */ { kKeyShift_R }, // VK_SHIFT
+ /* 0x111 */ { kKeyControl_R }, // VK_CONTROL
+ /* 0x112 */ { kKeyAlt_R }, // VK_MENU
+ /* 0x113 */ { kKeyNone }, // VK_PAUSE
+ /* 0x114 */ { kKeyNone }, // VK_CAPITAL
+ /* 0x115 */ { kKeyHangul }, // VK_HANGUL
+ /* 0x116 */ { kKeyNone }, // undefined
+ /* 0x117 */ { kKeyNone }, // VK_JUNJA
+ /* 0x118 */ { kKeyNone }, // VK_FINAL
+ /* 0x119 */ { kKeyHanja }, // VK_HANJA
+ /* 0x11a */ { kKeyNone }, // undefined
+ /* 0x11b */ { kKeyNone }, // VK_ESCAPE
+ /* 0x11c */ { kKeyNone }, // VK_CONVERT
+ /* 0x11d */ { kKeyNone }, // VK_NONCONVERT
+ /* 0x11e */ { kKeyNone }, // VK_ACCEPT
+ /* 0x11f */ { kKeyNone }, // VK_MODECHANGE
+ /* 0x120 */ { kKeyNone }, // VK_SPACE
+ /* 0x121 */ { kKeyPageUp }, // VK_PRIOR
+ /* 0x122 */ { kKeyPageDown }, // VK_NEXT
+ /* 0x123 */ { kKeyEnd }, // VK_END
+ /* 0x124 */ { kKeyHome }, // VK_HOME
+ /* 0x125 */ { kKeyLeft }, // VK_LEFT
+ /* 0x126 */ { kKeyUp }, // VK_UP
+ /* 0x127 */ { kKeyRight }, // VK_RIGHT
+ /* 0x128 */ { kKeyDown }, // VK_DOWN
+ /* 0x129 */ { kKeySelect }, // VK_SELECT
+ /* 0x12a */ { kKeyNone }, // VK_PRINT
+ /* 0x12b */ { kKeyExecute }, // VK_EXECUTE
+ /* 0x12c */ { kKeyPrint }, // VK_SNAPSHOT
+ /* 0x12d */ { kKeyInsert }, // VK_INSERT
+ /* 0x12e */ { kKeyDelete }, // VK_DELETE
+ /* 0x12f */ { kKeyHelp }, // VK_HELP
+ /* 0x130 */ { kKeyNone }, // VK_0
+ /* 0x131 */ { kKeyNone }, // VK_1
+ /* 0x132 */ { kKeyNone }, // VK_2
+ /* 0x133 */ { kKeyNone }, // VK_3
+ /* 0x134 */ { kKeyNone }, // VK_4
+ /* 0x135 */ { kKeyNone }, // VK_5
+ /* 0x136 */ { kKeyNone }, // VK_6
+ /* 0x137 */ { kKeyNone }, // VK_7
+ /* 0x138 */ { kKeyNone }, // VK_8
+ /* 0x139 */ { kKeyNone }, // VK_9
+ /* 0x13a */ { kKeyNone }, // undefined
+ /* 0x13b */ { kKeyNone }, // undefined
+ /* 0x13c */ { kKeyNone }, // undefined
+ /* 0x13d */ { kKeyNone }, // undefined
+ /* 0x13e */ { kKeyNone }, // undefined
+ /* 0x13f */ { kKeyNone }, // undefined
+ /* 0x140 */ { kKeyNone }, // undefined
+ /* 0x141 */ { kKeyNone }, // VK_A
+ /* 0x142 */ { kKeyNone }, // VK_B
+ /* 0x143 */ { kKeyNone }, // VK_C
+ /* 0x144 */ { kKeyNone }, // VK_D
+ /* 0x145 */ { kKeyNone }, // VK_E
+ /* 0x146 */ { kKeyNone }, // VK_F
+ /* 0x147 */ { kKeyNone }, // VK_G
+ /* 0x148 */ { kKeyNone }, // VK_H
+ /* 0x149 */ { kKeyNone }, // VK_I
+ /* 0x14a */ { kKeyNone }, // VK_J
+ /* 0x14b */ { kKeyNone }, // VK_K
+ /* 0x14c */ { kKeyNone }, // VK_L
+ /* 0x14d */ { kKeyNone }, // VK_M
+ /* 0x14e */ { kKeyNone }, // VK_N
+ /* 0x14f */ { kKeyNone }, // VK_O
+ /* 0x150 */ { kKeyNone }, // VK_P
+ /* 0x151 */ { kKeyNone }, // VK_Q
+ /* 0x152 */ { kKeyNone }, // VK_R
+ /* 0x153 */ { kKeyNone }, // VK_S
+ /* 0x154 */ { kKeyNone }, // VK_T
+ /* 0x155 */ { kKeyNone }, // VK_U
+ /* 0x156 */ { kKeyNone }, // VK_V
+ /* 0x157 */ { kKeyNone }, // VK_W
+ /* 0x158 */ { kKeyNone }, // VK_X
+ /* 0x159 */ { kKeyNone }, // VK_Y
+ /* 0x15a */ { kKeyNone }, // VK_Z
+ /* 0x15b */ { kKeySuper_L }, // VK_LWIN
+ /* 0x15c */ { kKeySuper_R }, // VK_RWIN
+ /* 0x15d */ { kKeyMenu }, // VK_APPS
+ /* 0x15e */ { kKeyNone }, // undefined
+ /* 0x15f */ { kKeyNone }, // VK_SLEEP
+ /* 0x160 */ { kKeyNone }, // VK_NUMPAD0
+ /* 0x161 */ { kKeyNone }, // VK_NUMPAD1
+ /* 0x162 */ { kKeyNone }, // VK_NUMPAD2
+ /* 0x163 */ { kKeyNone }, // VK_NUMPAD3
+ /* 0x164 */ { kKeyNone }, // VK_NUMPAD4
+ /* 0x165 */ { kKeyNone }, // VK_NUMPAD5
+ /* 0x166 */ { kKeyNone }, // VK_NUMPAD6
+ /* 0x167 */ { kKeyNone }, // VK_NUMPAD7
+ /* 0x168 */ { kKeyNone }, // VK_NUMPAD8
+ /* 0x169 */ { kKeyNone }, // VK_NUMPAD9
+ /* 0x16a */ { kKeyNone }, // VK_MULTIPLY
+ /* 0x16b */ { kKeyNone }, // VK_ADD
+ /* 0x16c */ { kKeyKP_Separator },// VK_SEPARATOR
+ /* 0x16d */ { kKeyNone }, // VK_SUBTRACT
+ /* 0x16e */ { kKeyNone }, // VK_DECIMAL
+ /* 0x16f */ { kKeyKP_Divide }, // VK_DIVIDE
+ /* 0x170 */ { kKeyNone }, // VK_F1
+ /* 0x171 */ { kKeyNone }, // VK_F2
+ /* 0x172 */ { kKeyNone }, // VK_F3
+ /* 0x173 */ { kKeyNone }, // VK_F4
+ /* 0x174 */ { kKeyNone }, // VK_F5
+ /* 0x175 */ { kKeyNone }, // VK_F6
+ /* 0x176 */ { kKeyNone }, // VK_F7
+ /* 0x177 */ { kKeyNone }, // VK_F8
+ /* 0x178 */ { kKeyNone }, // VK_F9
+ /* 0x179 */ { kKeyNone }, // VK_F10
+ /* 0x17a */ { kKeyNone }, // VK_F11
+ /* 0x17b */ { kKeyNone }, // VK_F12
+ /* 0x17c */ { kKeyF13 }, // VK_F13
+ /* 0x17d */ { kKeyF14 }, // VK_F14
+ /* 0x17e */ { kKeyF15 }, // VK_F15
+ /* 0x17f */ { kKeyF16 }, // VK_F16
+ /* 0x180 */ { kKeyF17 }, // VK_F17
+ /* 0x181 */ { kKeyF18 }, // VK_F18
+ /* 0x182 */ { kKeyF19 }, // VK_F19
+ /* 0x183 */ { kKeyF20 }, // VK_F20
+ /* 0x184 */ { kKeyF21 }, // VK_F21
+ /* 0x185 */ { kKeyF22 }, // VK_F22
+ /* 0x186 */ { kKeyF23 }, // VK_F23
+ /* 0x187 */ { kKeyF24 }, // VK_F24
+ /* 0x188 */ { kKeyNone }, // unassigned
+ /* 0x189 */ { kKeyNone }, // unassigned
+ /* 0x18a */ { kKeyNone }, // unassigned
+ /* 0x18b */ { kKeyNone }, // unassigned
+ /* 0x18c */ { kKeyNone }, // unassigned
+ /* 0x18d */ { kKeyNone }, // unassigned
+ /* 0x18e */ { kKeyNone }, // unassigned
+ /* 0x18f */ { kKeyNone }, // unassigned
+ /* 0x190 */ { kKeyNumLock }, // VK_NUMLOCK
+ /* 0x191 */ { kKeyNone }, // VK_SCROLL
+ /* 0x192 */ { kKeyNone }, // unassigned
+ /* 0x193 */ { kKeyNone }, // unassigned
+ /* 0x194 */ { kKeyNone }, // unassigned
+ /* 0x195 */ { kKeyNone }, // unassigned
+ /* 0x196 */ { kKeyNone }, // unassigned
+ /* 0x197 */ { kKeyNone }, // unassigned
+ /* 0x198 */ { kKeyNone }, // unassigned
+ /* 0x199 */ { kKeyNone }, // unassigned
+ /* 0x19a */ { kKeyNone }, // unassigned
+ /* 0x19b */ { kKeyNone }, // unassigned
+ /* 0x19c */ { kKeyNone }, // unassigned
+ /* 0x19d */ { kKeyNone }, // unassigned
+ /* 0x19e */ { kKeyNone }, // unassigned
+ /* 0x19f */ { kKeyNone }, // unassigned
+ /* 0x1a0 */ { kKeyShift_L }, // VK_LSHIFT
+ /* 0x1a1 */ { kKeyShift_R }, // VK_RSHIFT
+ /* 0x1a2 */ { kKeyControl_L }, // VK_LCONTROL
+ /* 0x1a3 */ { kKeyControl_R }, // VK_RCONTROL
+ /* 0x1a4 */ { kKeyAlt_L }, // VK_LMENU
+ /* 0x1a5 */ { kKeyAlt_R }, // VK_RMENU
+ /* 0x1a6 */ { kKeyWWWBack }, // VK_BROWSER_BACK
+ /* 0x1a7 */ { kKeyWWWForward }, // VK_BROWSER_FORWARD
+ /* 0x1a8 */ { kKeyWWWRefresh }, // VK_BROWSER_REFRESH
+ /* 0x1a9 */ { kKeyWWWStop }, // VK_BROWSER_STOP
+ /* 0x1aa */ { kKeyWWWSearch }, // VK_BROWSER_SEARCH
+ /* 0x1ab */ { kKeyWWWFavorites },// VK_BROWSER_FAVORITES
+ /* 0x1ac */ { kKeyWWWHome }, // VK_BROWSER_HOME
+ /* 0x1ad */ { kKeyAudioMute }, // VK_VOLUME_MUTE
+ /* 0x1ae */ { kKeyAudioDown }, // VK_VOLUME_DOWN
+ /* 0x1af */ { kKeyAudioUp }, // VK_VOLUME_UP
+ /* 0x1b0 */ { kKeyAudioNext }, // VK_MEDIA_NEXT_TRACK
+ /* 0x1b1 */ { kKeyAudioPrev }, // VK_MEDIA_PREV_TRACK
+ /* 0x1b2 */ { kKeyAudioStop }, // VK_MEDIA_STOP
+ /* 0x1b3 */ { kKeyAudioPlay }, // VK_MEDIA_PLAY_PAUSE
+ /* 0x1b4 */ { kKeyAppMail }, // VK_LAUNCH_MAIL
+ /* 0x1b5 */ { kKeyAppMedia }, // VK_LAUNCH_MEDIA_SELECT
+ /* 0x1b6 */ { kKeyAppUser1 }, // VK_LAUNCH_APP1
+ /* 0x1b7 */ { kKeyAppUser2 }, // VK_LAUNCH_APP2
+ /* 0x1b8 */ { kKeyNone }, // unassigned
+ /* 0x1b9 */ { kKeyNone }, // unassigned
+ /* 0x1ba */ { kKeyNone }, // OEM specific
+ /* 0x1bb */ { kKeyNone }, // OEM specific
+ /* 0x1bc */ { kKeyNone }, // OEM specific
+ /* 0x1bd */ { kKeyNone }, // OEM specific
+ /* 0x1be */ { kKeyNone }, // OEM specific
+ /* 0x1bf */ { kKeyNone }, // OEM specific
+ /* 0x1c0 */ { kKeyNone }, // OEM specific
+ /* 0x1c1 */ { kKeyNone }, // unassigned
+ /* 0x1c2 */ { kKeyNone }, // unassigned
+ /* 0x1c3 */ { kKeyNone }, // unassigned
+ /* 0x1c4 */ { kKeyNone }, // unassigned
+ /* 0x1c5 */ { kKeyNone }, // unassigned
+ /* 0x1c6 */ { kKeyNone }, // unassigned
+ /* 0x1c7 */ { kKeyNone }, // unassigned
+ /* 0x1c8 */ { kKeyNone }, // unassigned
+ /* 0x1c9 */ { kKeyNone }, // unassigned
+ /* 0x1ca */ { kKeyNone }, // unassigned
+ /* 0x1cb */ { kKeyNone }, // unassigned
+ /* 0x1cc */ { kKeyNone }, // unassigned
+ /* 0x1cd */ { kKeyNone }, // unassigned
+ /* 0x1ce */ { kKeyNone }, // unassigned
+ /* 0x1cf */ { kKeyNone }, // unassigned
+ /* 0x1d0 */ { kKeyNone }, // unassigned
+ /* 0x1d1 */ { kKeyNone }, // unassigned
+ /* 0x1d2 */ { kKeyNone }, // unassigned
+ /* 0x1d3 */ { kKeyNone }, // unassigned
+ /* 0x1d4 */ { kKeyNone }, // unassigned
+ /* 0x1d5 */ { kKeyNone }, // unassigned
+ /* 0x1d6 */ { kKeyNone }, // unassigned
+ /* 0x1d7 */ { kKeyNone }, // unassigned
+ /* 0x1d8 */ { kKeyNone }, // unassigned
+ /* 0x1d9 */ { kKeyNone }, // unassigned
+ /* 0x1da */ { kKeyNone }, // unassigned
+ /* 0x1db */ { kKeyNone }, // OEM specific
+ /* 0x1dc */ { kKeyNone }, // OEM specific
+ /* 0x1dd */ { kKeyNone }, // OEM specific
+ /* 0x1de */ { kKeyNone }, // OEM specific
+ /* 0x1df */ { kKeyNone }, // OEM specific
+ /* 0x1e0 */ { kKeyNone }, // OEM specific
+ /* 0x1e1 */ { kKeyNone }, // OEM specific
+ /* 0x1e2 */ { kKeyNone }, // OEM specific
+ /* 0x1e3 */ { kKeyNone }, // OEM specific
+ /* 0x1e4 */ { kKeyNone }, // OEM specific
+ /* 0x1e5 */ { kKeyNone }, // unassigned
+ /* 0x1e6 */ { kKeyNone }, // OEM specific
+ /* 0x1e7 */ { kKeyNone }, // unassigned
+ /* 0x1e8 */ { kKeyNone }, // unassigned
+ /* 0x1e9 */ { kKeyNone }, // OEM specific
+ /* 0x1ea */ { kKeyNone }, // OEM specific
+ /* 0x1eb */ { kKeyNone }, // OEM specific
+ /* 0x1ec */ { kKeyNone }, // OEM specific
+ /* 0x1ed */ { kKeyNone }, // OEM specific
+ /* 0x1ee */ { kKeyNone }, // OEM specific
+ /* 0x1ef */ { kKeyNone }, // OEM specific
+ /* 0x1f0 */ { kKeyNone }, // OEM specific
+ /* 0x1f1 */ { kKeyNone }, // OEM specific
+ /* 0x1f2 */ { kKeyNone }, // VK_OEM_COPY
+ /* 0x1f3 */ { kKeyNone }, // VK_OEM_AUTO
+ /* 0x1f4 */ { kKeyNone }, // VK_OEM_ENLW
+ /* 0x1f5 */ { kKeyNone }, // OEM specific
+ /* 0x1f6 */ { kKeyNone }, // VK_ATTN
+ /* 0x1f7 */ { kKeyNone }, // VK_CRSEL
+ /* 0x1f8 */ { kKeyNone }, // VK_EXSEL
+ /* 0x1f9 */ { kKeyNone }, // VK_EREOF
+ /* 0x1fa */ { kKeyNone }, // VK_PLAY
+ /* 0x1fb */ { kKeyNone }, // VK_ZOOM
+ /* 0x1fc */ { kKeyNone }, // reserved
+ /* 0x1fd */ { kKeyNone }, // VK_PA1
+ /* 0x1fe */ { kKeyNone }, // VK_OEM_CLEAR
+ /* 0x1ff */ { kKeyNone } // reserved
+};
+
+struct Win32Modifiers {
+public:
+ UINT m_vk;
+ KeyModifierMask m_mask;
+};
+
+static const Win32Modifiers s_modifiers[] =
+{
+ { VK_SHIFT, KeyModifierShift },
+ { VK_LSHIFT, KeyModifierShift },
+ { VK_RSHIFT, KeyModifierShift },
+ { VK_CONTROL, KeyModifierControl },
+ { VK_LCONTROL, KeyModifierControl },
+ { VK_RCONTROL, KeyModifierControl },
+ { VK_MENU, KeyModifierAlt },
+ { VK_LMENU, KeyModifierAlt },
+ { VK_RMENU, KeyModifierAlt },
+ { VK_LWIN, KeyModifierSuper },
+ { VK_RWIN, KeyModifierSuper }
+};
+
+MSWindowsKeyState::MSWindowsKeyState(
+ MSWindowsDesks* desks, void* eventTarget, IEventQueue* events) :
+ KeyState(events),
+ m_eventTarget(eventTarget),
+ m_desks(desks),
+ m_keyLayout(GetKeyboardLayout(0)),
+ m_fixTimer(NULL),
+ m_lastDown(0),
+ m_useSavedModifiers(false),
+ m_savedModifiers(0),
+ m_originalSavedModifiers(0),
+ m_events(events)
+{
+ init();
+}
+
+MSWindowsKeyState::MSWindowsKeyState(
+ MSWindowsDesks* desks, void* eventTarget, IEventQueue* events, barrier::KeyMap& keyMap) :
+ KeyState(events, keyMap),
+ m_eventTarget(eventTarget),
+ m_desks(desks),
+ m_keyLayout(GetKeyboardLayout(0)),
+ m_fixTimer(NULL),
+ m_lastDown(0),
+ m_useSavedModifiers(false),
+ m_savedModifiers(0),
+ m_originalSavedModifiers(0),
+ m_events(events)
+{
+ init();
+}
+
+MSWindowsKeyState::~MSWindowsKeyState()
+{
+ disable();
+}
+
+void
+MSWindowsKeyState::init()
+{
+ // look up symbol that's available on winNT family but not win95
+ HMODULE userModule = GetModuleHandle("user32.dll");
+ m_ToUnicodeEx = (ToUnicodeEx_t)GetProcAddress(userModule, "ToUnicodeEx");
+}
+
+void
+MSWindowsKeyState::disable()
+{
+ if (m_fixTimer != NULL) {
+ m_events->removeHandler(Event::kTimer, m_fixTimer);
+ m_events->deleteTimer(m_fixTimer);
+ m_fixTimer = NULL;
+ }
+ m_lastDown = 0;
+}
+
+KeyButton
+MSWindowsKeyState::virtualKeyToButton(UINT virtualKey) const
+{
+ return m_virtualKeyToButton[virtualKey & 0xffu];
+}
+
+void
+MSWindowsKeyState::setKeyLayout(HKL keyLayout)
+{
+ m_keyLayout = keyLayout;
+}
+
+bool
+MSWindowsKeyState::testAutoRepeat(bool press, bool isRepeat, KeyButton button)
+{
+ if (!isRepeat) {
+ isRepeat = (press && m_lastDown != 0 && button == m_lastDown);
+ }
+ if (press) {
+ m_lastDown = button;
+ }
+ else {
+ m_lastDown = 0;
+ }
+ return isRepeat;
+}
+
+void
+MSWindowsKeyState::saveModifiers()
+{
+ m_savedModifiers = getActiveModifiers();
+ m_originalSavedModifiers = m_savedModifiers;
+}
+
+void
+MSWindowsKeyState::useSavedModifiers(bool enable)
+{
+ if (enable != m_useSavedModifiers) {
+ m_useSavedModifiers = enable;
+ if (!m_useSavedModifiers) {
+ // transfer any modifier state changes to KeyState's state
+ KeyModifierMask mask = m_originalSavedModifiers ^ m_savedModifiers;
+ getActiveModifiersRValue() =
+ (getActiveModifiers() & ~mask) | (m_savedModifiers & mask);
+ }
+ }
+}
+
+KeyID
+MSWindowsKeyState::mapKeyFromEvent(WPARAM charAndVirtKey,
+ LPARAM info, KeyModifierMask* maskOut) const
+{
+ static const KeyModifierMask s_controlAlt =
+ KeyModifierControl | KeyModifierAlt;
+
+ // extract character, virtual key, and if we didn't use AltGr
+ char c = (char)((charAndVirtKey & 0xff00u) >> 8);
+ UINT vkCode = (charAndVirtKey & 0xffu);
+ bool noAltGr = ((charAndVirtKey & 0xff0000u) != 0);
+
+ // handle some keys via table lookup
+ KeyID id = getKeyID(vkCode, (KeyButton)((info >> 16) & 0x1ffu));
+
+ // check if not in table; map character to key id
+ if (id == kKeyNone && c != 0) {
+ if ((c & 0x80u) == 0) {
+ // ASCII
+ id = static_cast<KeyID>(c) & 0xffu;
+ }
+ else {
+ // character is not really ASCII. instead it's some
+ // character in the current ANSI code page. try to
+ // convert that to a Unicode character. if we fail
+ // then use the single byte character as is.
+ char src = c;
+ wchar_t unicode;
+ if (MultiByteToWideChar(CP_THREAD_ACP, MB_PRECOMPOSED,
+ &src, 1, &unicode, 1) > 0) {
+ id = static_cast<KeyID>(unicode);
+ }
+ else {
+ id = static_cast<KeyID>(c) & 0xffu;
+ }
+ }
+ }
+
+ // set modifier mask
+ if (maskOut != NULL) {
+ KeyModifierMask active = getActiveModifiers();
+ if (!noAltGr && (active & s_controlAlt) == s_controlAlt) {
+ // if !noAltGr then we're only interested in matching the
+ // key, not the AltGr. AltGr is down (i.e. control and alt
+ // are down) but we don't want the client to have to match
+ // that so we clear it.
+ active &= ~s_controlAlt;
+ }
+ if (id == kKeyHangul) {
+ // If shift-space is used to change input mode, clear shift modifier.
+ active &= ~KeyModifierShift;
+ }
+ *maskOut = active;
+ }
+
+ return id;
+}
+
+bool
+MSWindowsKeyState::didGroupsChange() const
+{
+ GroupList groups;
+ return (getGroups(groups) && groups != m_groups);
+}
+
+UINT
+MSWindowsKeyState::mapKeyToVirtualKey(KeyID key) const
+{
+ if (key == kKeyNone) {
+ return 0;
+ }
+ KeyToVKMap::const_iterator i = m_keyToVKMap.find(key);
+ if (i == m_keyToVKMap.end()) {
+ return 0;
+ }
+ else {
+ return i->second;
+ }
+}
+
+void
+MSWindowsKeyState::onKey(KeyButton button, bool down, KeyModifierMask newState)
+{
+ KeyState::onKey(button, down, newState);
+}
+
+void
+MSWindowsKeyState::sendKeyEvent(void* target,
+ bool press, bool isAutoRepeat,
+ KeyID key, KeyModifierMask mask,
+ SInt32 count, KeyButton button)
+{
+ if (press || isAutoRepeat) {
+ // send key
+ if (press && !isAutoRepeat) {
+ KeyState::sendKeyEvent(target, true, false,
+ key, mask, 1, button);
+ if (count > 0) {
+ --count;
+ }
+ }
+ if (count >= 1) {
+ KeyState::sendKeyEvent(target, true, true,
+ key, mask, count, button);
+ }
+ }
+ else {
+ // do key up
+ KeyState::sendKeyEvent(target, false, false, key, mask, 1, button);
+ }
+}
+
+void
+MSWindowsKeyState::fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button)
+{
+ KeyState::fakeKeyDown(id, mask, button);
+}
+
+bool
+MSWindowsKeyState::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button)
+{
+ return KeyState::fakeKeyRepeat(id, mask, count, button);
+}
+
+bool
+MSWindowsKeyState::fakeCtrlAltDel()
+{
+ // to fake ctrl+alt+del on the NT family we broadcast a suitable
+ // hotkey to all windows on the winlogon desktop. however, the
+ // current thread must be on that desktop to do the broadcast
+ // and we can't switch just any thread because some own windows
+ // or hooks. so start a new thread to do the real work.
+ HANDLE hEvtSendSas = OpenEvent(EVENT_MODIFY_STATE, FALSE, "Global\\SendSAS");
+ if (hEvtSendSas) {
+ LOG((CLOG_DEBUG "found the SendSAS event - signaling my launcher to simulate ctrl+alt+del"));
+ SetEvent(hEvtSendSas);
+ CloseHandle(hEvtSendSas);
+ }
+ else {
+ Thread cad(new FunctionJob(&MSWindowsKeyState::ctrlAltDelThread));
+ cad.wait();
+ }
+
+ return true;
+}
+
+void
+MSWindowsKeyState::ctrlAltDelThread(void*)
+{
+ // get the Winlogon desktop at whatever privilege we can
+ HDESK desk = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED);
+ if (desk != NULL) {
+ if (SetThreadDesktop(desk)) {
+ PostMessage(HWND_BROADCAST, WM_HOTKEY, 0,
+ MAKELPARAM(MOD_CONTROL | MOD_ALT, VK_DELETE));
+ }
+ else {
+ LOG((CLOG_DEBUG "can't switch to Winlogon desk: %d", GetLastError()));
+ }
+ CloseDesktop(desk);
+ }
+ else {
+ LOG((CLOG_DEBUG "can't open Winlogon desk: %d", GetLastError()));
+ }
+}
+
+KeyModifierMask
+MSWindowsKeyState::pollActiveModifiers() const
+{
+ KeyModifierMask state = 0;
+
+ // get non-toggle modifiers from our own shadow key state
+ for (size_t i = 0; i < sizeof(s_modifiers) / sizeof(s_modifiers[0]); ++i) {
+ KeyButton button = virtualKeyToButton(s_modifiers[i].m_vk);
+ if (button != 0 && isKeyDown(button)) {
+ state |= s_modifiers[i].m_mask;
+ }
+ }
+
+ // we can get toggle modifiers from the system
+ if ((GetKeyState(VK_CAPITAL) & 0x01) != 0) {
+ state |= KeyModifierCapsLock;
+ }
+ if ((GetKeyState(VK_NUMLOCK) & 0x01) != 0) {
+ state |= KeyModifierNumLock;
+ }
+ if ((GetKeyState(VK_SCROLL) & 0x01) != 0) {
+ state |= KeyModifierScrollLock;
+ }
+
+ return state;
+}
+
+SInt32
+MSWindowsKeyState::pollActiveGroup() const
+{
+ // determine the thread that'll receive this event
+ HWND targetWindow = GetForegroundWindow();
+ DWORD targetThread = GetWindowThreadProcessId(targetWindow, NULL);
+
+ // get keyboard layout for the thread
+ HKL hkl = GetKeyboardLayout(targetThread);
+
+ if (!hkl) {
+ // GetKeyboardLayout failed. Maybe targetWindow is a console window.
+ // We're getting the keyboard layout of the desktop instead.
+ targetWindow = GetDesktopWindow();
+ targetThread = GetWindowThreadProcessId(targetWindow, NULL);
+ hkl = GetKeyboardLayout(targetThread);
+ }
+
+ // get group
+ GroupMap::const_iterator i = m_groupMap.find(hkl);
+ if (i == m_groupMap.end()) {
+ LOG((CLOG_DEBUG1 "can't find keyboard layout %08x", hkl));
+ return 0;
+ }
+
+ return i->second;
+}
+
+void
+MSWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const
+{
+ BYTE keyState[256];
+ if (!GetKeyboardState(keyState)) {
+ LOG((CLOG_ERR "GetKeyboardState returned false on pollPressedKeys"));
+ return;
+ }
+ for (KeyButton i = 1; i < 256; ++i) {
+ if ((keyState[i] & 0x80) != 0) {
+ KeyButton keyButton = virtualKeyToButton(i);
+ if (keyButton != 0) {
+ pressedKeys.insert(keyButton);
+ }
+ }
+ }
+}
+
+void
+MSWindowsKeyState::getKeyMap(barrier::KeyMap& keyMap)
+{
+ // update keyboard groups
+ if (getGroups(m_groups)) {
+ m_groupMap.clear();
+ SInt32 numGroups = (SInt32)m_groups.size();
+ for (SInt32 g = 0; g < numGroups; ++g) {
+ m_groupMap[m_groups[g]] = g;
+ }
+ }
+ HKL activeLayout = GetKeyboardLayout(0);
+
+ // clear table
+ memset(m_virtualKeyToButton, 0, sizeof(m_virtualKeyToButton));
+ m_keyToVKMap.clear();
+
+ barrier::KeyMap::KeyItem item;
+ SInt32 numGroups = (SInt32)m_groups.size();
+ for (SInt32 g = 0; g < numGroups; ++g) {
+ item.m_group = g;
+ ActivateKeyboardLayout(m_groups[g], 0);
+
+ // clear tables
+ memset(m_buttonToVK, 0, sizeof(m_buttonToVK));
+ memset(m_buttonToNumpadVK, 0, sizeof(m_buttonToNumpadVK));
+
+ // map buttons (scancodes) to virtual keys
+ for (KeyButton i = 1; i < 256; ++i) {
+ UINT vk = MapVirtualKey(i, 1);
+ if (vk == 0) {
+ // unmapped
+ continue;
+ }
+
+ // deal with certain virtual keys specially
+ switch (vk) {
+ case VK_SHIFT:
+ // this is important for sending the correct modifier to the
+ // client, a patch from bug #242 (right shift broken for ms
+ // remote desktop) removed this to just use left shift, which
+ // caused bug #2799 (right shift broken for osx).
+ // we must not repeat this same mistake and must fix platform
+ // specific bugs in code that only affects that platform.
+ if (MapVirtualKey(VK_RSHIFT, 0) == i) {
+ vk = VK_RSHIFT;
+ }
+ else {
+ vk = VK_LSHIFT;
+ }
+ break;
+
+ case VK_CONTROL:
+ vk = VK_LCONTROL;
+ break;
+
+ case VK_MENU:
+ vk = VK_LMENU;
+ break;
+
+ case VK_NUMLOCK:
+ vk = VK_PAUSE;
+ break;
+
+ case VK_NUMPAD0:
+ case VK_NUMPAD1:
+ case VK_NUMPAD2:
+ case VK_NUMPAD3:
+ case VK_NUMPAD4:
+ case VK_NUMPAD5:
+ case VK_NUMPAD6:
+ case VK_NUMPAD7:
+ case VK_NUMPAD8:
+ case VK_NUMPAD9:
+ case VK_DECIMAL:
+ // numpad keys are saved in their own table
+ m_buttonToNumpadVK[i] = vk;
+ continue;
+
+ case VK_LWIN:
+ case VK_RWIN:
+ break;
+
+ case VK_RETURN:
+ case VK_PRIOR:
+ case VK_NEXT:
+ case VK_END:
+ case VK_HOME:
+ case VK_LEFT:
+ case VK_UP:
+ case VK_RIGHT:
+ case VK_DOWN:
+ case VK_INSERT:
+ case VK_DELETE:
+ // also add extended key for these
+ m_buttonToVK[i | 0x100u] = vk;
+ break;
+ }
+
+ if (m_buttonToVK[i] == 0) {
+ m_buttonToVK[i] = vk;
+ }
+ }
+
+ // now map virtual keys to buttons. multiple virtual keys may map
+ // to a single button. if the virtual key matches the one in
+ // m_buttonToVK then we use the button as is. if not then it's
+ // either a numpad key and we use the button as is or it's an
+ // extended button.
+ for (UINT i = 1; i < 255; ++i) {
+ // skip virtual keys we don't want
+ switch (i) {
+ case VK_LBUTTON:
+ case VK_RBUTTON:
+ case VK_MBUTTON:
+ case VK_XBUTTON1:
+ case VK_XBUTTON2:
+ case VK_SHIFT:
+ case VK_CONTROL:
+ case VK_MENU:
+ continue;
+ }
+
+ // get the button
+ KeyButton button = static_cast<KeyButton>(MapVirtualKey(i, 0));
+ if (button == 0) {
+ continue;
+ }
+
+ // deal with certain virtual keys specially
+ switch (i) {
+ case VK_NUMPAD0:
+ case VK_NUMPAD1:
+ case VK_NUMPAD2:
+ case VK_NUMPAD3:
+ case VK_NUMPAD4:
+ case VK_NUMPAD5:
+ case VK_NUMPAD6:
+ case VK_NUMPAD7:
+ case VK_NUMPAD8:
+ case VK_NUMPAD9:
+ case VK_DECIMAL:
+ m_buttonToNumpadVK[button] = i;
+ break;
+
+ default:
+ // add extended key if virtual keys don't match
+ if (m_buttonToVK[button] != i) {
+ m_buttonToVK[button | 0x100u] = i;
+ }
+ break;
+ }
+ }
+
+ // set virtual key to button table
+ if (activeLayout == m_groups[g]) {
+ for (KeyButton i = 0; i < 512; ++i) {
+ if (m_buttonToVK[i] != 0) {
+ if (m_virtualKeyToButton[m_buttonToVK[i]] == 0) {
+ m_virtualKeyToButton[m_buttonToVK[i]] = i;
+ }
+ }
+ if (m_buttonToNumpadVK[i] != 0) {
+ if (m_virtualKeyToButton[m_buttonToNumpadVK[i]] == 0) {
+ m_virtualKeyToButton[m_buttonToNumpadVK[i]] = i;
+ }
+ }
+ }
+ }
+
+ // add numpad keys
+ for (KeyButton i = 0; i < 512; ++i) {
+ if (m_buttonToNumpadVK[i] != 0) {
+ item.m_id = getKeyID(m_buttonToNumpadVK[i], i);
+ item.m_button = i;
+ item.m_required = KeyModifierNumLock;
+ item.m_sensitive = KeyModifierNumLock | KeyModifierShift;
+ item.m_generates = 0;
+ item.m_client = m_buttonToNumpadVK[i];
+ addKeyEntry(keyMap, item);
+ }
+ }
+
+ // add other keys
+ BYTE keys[256];
+ memset(keys, 0, sizeof(keys));
+ for (KeyButton i = 0; i < 512; ++i) {
+ if (m_buttonToVK[i] != 0) {
+ // initialize item
+ item.m_id = getKeyID(m_buttonToVK[i], i);
+ item.m_button = i;
+ item.m_required = 0;
+ item.m_sensitive = 0;
+ item.m_client = m_buttonToVK[i];
+
+ // get flags for modifier keys
+ barrier::KeyMap::initModifierKey(item);
+
+ if (item.m_id == 0) {
+ // translate virtual key to a character with and without
+ // shift, caps lock, and AltGr.
+ struct Modifier {
+ UINT m_vk1;
+ UINT m_vk2;
+ BYTE m_state;
+ KeyModifierMask m_mask;
+ };
+ static const Modifier modifiers[] = {
+ { VK_SHIFT, VK_SHIFT, 0x80u, KeyModifierShift },
+ { VK_CAPITAL, VK_CAPITAL, 0x01u, KeyModifierCapsLock },
+ { VK_CONTROL, VK_MENU, 0x80u, KeyModifierControl |
+ KeyModifierAlt }
+ };
+ static const size_t s_numModifiers =
+ sizeof(modifiers) / sizeof(modifiers[0]);
+ static const size_t s_numCombinations = 1 << s_numModifiers;
+ KeyID id[s_numCombinations];
+
+ bool anyFound = false;
+ KeyButton button = static_cast<KeyButton>(i & 0xffu);
+ for (size_t j = 0; j < s_numCombinations; ++j) {
+ for (size_t k = 0; k < s_numModifiers; ++k) {
+ //if ((j & (1 << k)) != 0) {
+ // http://msdn.microsoft.com/en-us/library/ke55d167.aspx
+ if ((j & (1i64 << k)) != 0) {
+ keys[modifiers[k].m_vk1] = modifiers[k].m_state;
+ keys[modifiers[k].m_vk2] = modifiers[k].m_state;
+ }
+ else {
+ keys[modifiers[k].m_vk1] = 0;
+ keys[modifiers[k].m_vk2] = 0;
+ }
+ }
+ id[j] = getIDForKey(item, button,
+ m_buttonToVK[i], keys, m_groups[g]);
+ if (id[j] != 0) {
+ anyFound = true;
+ }
+ }
+
+ if (anyFound) {
+ // determine what modifiers we're sensitive to.
+ // we're sensitive if the KeyID changes when the
+ // modifier does.
+ item.m_sensitive = 0;
+ for (size_t k = 0; k < s_numModifiers; ++k) {
+ for (size_t j = 0; j < s_numCombinations; ++j) {
+ //if (id[j] != id[j ^ (1u << k)]) {
+ // http://msdn.microsoft.com/en-us/library/ke55d167.aspx
+ if (id[j] != id[j ^ (1ui64 << k)]) {
+ item.m_sensitive |= modifiers[k].m_mask;
+ break;
+ }
+ }
+ }
+
+ // save each key. the map will automatically discard
+ // duplicates, like an unshift and shifted version of
+ // a key that's insensitive to shift.
+ for (size_t j = 0; j < s_numCombinations; ++j) {
+ item.m_id = id[j];
+ item.m_required = 0;
+ for (size_t k = 0; k < s_numModifiers; ++k) {
+ if ((j & (1i64 << k)) != 0) {
+ item.m_required |= modifiers[k].m_mask;
+ }
+ }
+ addKeyEntry(keyMap, item);
+ }
+ }
+ }
+ else {
+ // found in table
+ switch (m_buttonToVK[i]) {
+ case VK_TAB:
+ // add kKeyLeftTab, too
+ item.m_id = kKeyLeftTab;
+ item.m_required |= KeyModifierShift;
+ item.m_sensitive |= KeyModifierShift;
+ addKeyEntry(keyMap, item);
+ item.m_id = kKeyTab;
+ item.m_required &= ~KeyModifierShift;
+ break;
+
+ case VK_CANCEL:
+ item.m_required |= KeyModifierControl;
+ item.m_sensitive |= KeyModifierControl;
+ break;
+
+ case VK_SNAPSHOT:
+ item.m_sensitive |= KeyModifierAlt;
+ if ((i & 0x100u) == 0) {
+ // non-extended snapshot key requires alt
+ item.m_required |= KeyModifierAlt;
+ }
+ break;
+ }
+ addKeyEntry(keyMap, item);
+ }
+ }
+ }
+ }
+
+ // restore keyboard layout
+ ActivateKeyboardLayout(activeLayout, 0);
+}
+
+void
+MSWindowsKeyState::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"));
+ KeyButton button = keystroke.m_data.m_button.m_button;
+
+ // windows doesn't send key ups for key repeats
+ if (keystroke.m_data.m_button.m_repeat &&
+ !keystroke.m_data.m_button.m_press) {
+ LOG((CLOG_DEBUG1 " discard key repeat release"));
+ break;
+ }
+
+ // get the virtual key for the button
+ UINT vk = keystroke.m_data.m_button.m_client;
+
+ // special handling of VK_SNAPSHOT
+ if (vk == VK_SNAPSHOT) {
+ if ((getActiveModifiers() & KeyModifierAlt) != 0) {
+ // snapshot active window
+ button = 1;
+ }
+ else {
+ // snapshot full screen
+ button = 0;
+ }
+ }
+
+ // synthesize event
+ m_desks->fakeKeyEvent(button, vk,
+ keystroke.m_data.m_button.m_press,
+ keystroke.m_data.m_button.m_repeat);
+ break;
+ }
+
+ case Keystroke::kGroup:
+ // we don't restore the group. we'd like to but we can't be
+ // sure the restoring group change will be processed after the
+ // key events.
+ if (!keystroke.m_data.m_group.m_restore) {
+ if (keystroke.m_data.m_group.m_absolute) {
+ LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group));
+ setWindowGroup(keystroke.m_data.m_group.m_group);
+ }
+ else {
+ LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group));
+ setWindowGroup(getEffectiveGroup(pollActiveGroup(),
+ keystroke.m_data.m_group.m_group));
+ }
+ }
+ break;
+ }
+}
+
+KeyModifierMask&
+MSWindowsKeyState::getActiveModifiersRValue()
+{
+ if (m_useSavedModifiers) {
+ return m_savedModifiers;
+ }
+ else {
+ return KeyState::getActiveModifiersRValue();
+ }
+}
+
+bool
+MSWindowsKeyState::getGroups(GroupList& groups) const
+{
+ // get keyboard layouts
+ UInt32 newNumLayouts = GetKeyboardLayoutList(0, NULL);
+ if (newNumLayouts == 0) {
+ LOG((CLOG_DEBUG1 "can't get keyboard layouts"));
+ return false;
+ }
+ HKL* newLayouts = new HKL[newNumLayouts];
+ newNumLayouts = GetKeyboardLayoutList(newNumLayouts, newLayouts);
+ if (newNumLayouts == 0) {
+ LOG((CLOG_DEBUG1 "can't get keyboard layouts"));
+ delete[] newLayouts;
+ return false;
+ }
+
+ groups.clear();
+ groups.insert(groups.end(), newLayouts, newLayouts + newNumLayouts);
+ delete[] newLayouts;
+ return true;
+}
+
+void
+MSWindowsKeyState::setWindowGroup(SInt32 group)
+{
+ HWND targetWindow = GetForegroundWindow();
+
+ bool sysCharSet = true;
+ // 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]);
+
+ // 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
+ // necessary since the messages should arrive in order. if we don't
+ // delay, though, some of our keyboard events may disappear.
+ Sleep(100);
+}
+
+KeyID
+MSWindowsKeyState::getKeyID(UINT virtualKey, KeyButton button) const
+{
+ // Some virtual keycodes have same values.
+ // VK_HANGUL == VK_KANA, VK_HANJA == NK_KANJI
+ // which are used to change the input mode of IME.
+ // But they have different X11 keysym. So we should distinguish them.
+ if ((LOWORD(m_keyLayout) & 0xffffu) == 0x0412u) { // 0x0412 : Korean Locale ID
+ if (virtualKey == VK_HANGUL || virtualKey == VK_HANJA) {
+ // If shift-space is used to change the input mode,
+ // the extented bit is not set. So add it to get right key id.
+ button |= 0x100u;
+ }
+ }
+
+ if ((button & 0x100u) != 0) {
+ virtualKey += 0x100u;
+ }
+ return s_virtualKey[virtualKey];
+}
+
+UINT
+MSWindowsKeyState::mapButtonToVirtualKey(KeyButton button) const
+{
+ return m_buttonToVK[button];
+}
+
+KeyID
+MSWindowsKeyState::getIDForKey(barrier::KeyMap::KeyItem& item,
+ KeyButton button, UINT virtualKey,
+ PBYTE keyState, HKL hkl) const
+{
+ WCHAR unicode[2];
+ int n = m_ToUnicodeEx(
+ virtualKey, button, keyState, unicode,
+ sizeof(unicode) / sizeof(unicode[0]), 0, hkl);
+ KeyID id = static_cast<KeyID>(unicode[0]);
+
+ switch (n) {
+ case -1:
+ return barrier::KeyMap::getDeadKey(id);
+
+ default:
+ case 0:
+ // unmapped
+ return kKeyNone;
+
+ case 1:
+ return id;
+
+ case 2:
+ // left over dead key in buffer. this used to recurse,
+ // but apparently this causes a stack overflow, so just
+ // return no key instead.
+ return kKeyNone;
+ }
+}
+
+void
+MSWindowsKeyState::addKeyEntry(barrier::KeyMap& keyMap, barrier::KeyMap::KeyItem& item)
+{
+ keyMap.addKeyEntry(item);
+ if (item.m_group == 0) {
+ m_keyToVKMap[item.m_id] = static_cast<UINT>(item.m_client);
+ }
+}
+
diff --git a/src/lib/platform/MSWindowsKeyState.h b/src/lib/platform/MSWindowsKeyState.h
new file mode 100644
index 0000000..b226d8b
--- /dev/null
+++ b/src/lib/platform/MSWindowsKeyState.h
@@ -0,0 +1,233 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/KeyState.h"
+#include "base/String.h"
+#include "common/stdvector.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+class Event;
+class EventQueueTimer;
+class MSWindowsDesks;
+class IEventQueue;
+
+//! Microsoft Windows key mapper
+/*!
+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, barrier::KeyMap& keyMap);
+ virtual ~MSWindowsKeyState();
+
+ //! @name manipulators
+ //@{
+
+ //! Handle screen disabling
+ /*!
+ Called when screen is disabled. This is needed to deal with platform
+ brokenness.
+ */
+ void disable();
+
+ //! Set the active keyboard layout
+ /*!
+ Uses \p keyLayout when querying the keyboard.
+ */
+ void setKeyLayout(HKL keyLayout);
+
+ //! Test and set autorepeat state
+ /*!
+ Returns true if the given button is autorepeating and updates internal
+ state.
+ */
+ bool testAutoRepeat(bool press, bool isRepeat, KeyButton);
+
+ //! Remember modifier state
+ /*!
+ Records the current non-toggle modifier state.
+ */
+ void saveModifiers();
+
+ //! Set effective modifier state
+ /*!
+ Temporarily sets the non-toggle modifier state to those saved by the
+ last call to \c saveModifiers if \p enable is \c true. Restores the
+ modifier state to the current modifier state if \p enable is \c false.
+ This is for synthesizing keystrokes on the primary screen when the
+ cursor is on a secondary screen. When on a secondary screen we capture
+ all non-toggle modifier state, track the state internally and do not
+ pass it on. So if Alt+F1 synthesizes Alt+X we need to synthesize
+ not just X but also Alt, despite the fact that our internal modifier
+ state indicates Alt is down, because local apps never saw the Alt down
+ event.
+ */
+ void useSavedModifiers(bool enable);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Map a virtual key to a button
+ /*!
+ Returns the button for the \p virtualKey.
+ */
+ KeyButton virtualKeyToButton(UINT virtualKey) const;
+
+ //! Map key event to a key
+ /*!
+ Converts a key event into a KeyID and the shadow modifier state
+ to a modifier mask.
+ */
+ KeyID mapKeyFromEvent(WPARAM charAndVirtKey,
+ LPARAM info, KeyModifierMask* maskOut) const;
+
+ //! Check if keyboard groups have changed
+ /*!
+ Returns true iff the number or order of the keyboard groups have
+ changed since the last call to updateKeys().
+ */
+ bool didGroupsChange() const;
+
+ //! Map key to virtual key
+ /*!
+ Returns the virtual key for key \p key or 0 if there's no such virtual
+ key.
+ */
+ UINT mapKeyToVirtualKey(KeyID key) const;
+
+ //! Map virtual key and button to KeyID
+ /*!
+ Returns the KeyID for virtual key \p virtualKey and button \p button
+ (button should include the extended key bit), or kKeyNone if there is
+ no such key.
+ */
+ KeyID getKeyID(UINT virtualKey, KeyButton button) const;
+
+ //! Map button to virtual key
+ /*!
+ Returns the virtual key for button \p button
+ (button should include the extended key bit), or kKeyNone if there is
+ no such key.
+ */
+ UINT mapButtonToVirtualKey(KeyButton button) const;
+
+ //@}
+
+ // IKeyState overrides
+ virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button);
+ virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button);
+ virtual bool fakeCtrlAltDel();
+ virtual KeyModifierMask
+ pollActiveModifiers() const;
+ virtual SInt32 pollActiveGroup() const;
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const;
+
+ // KeyState overrides
+ virtual void onKey(KeyButton button, bool down,
+ KeyModifierMask newState);
+ virtual void sendKeyEvent(void* target,
+ bool press, bool isAutoRepeat,
+ KeyID key, KeyModifierMask mask,
+ SInt32 count, KeyButton button);
+
+ // Unit test accessors
+ KeyButton getLastDown() const { return m_lastDown; }
+ void setLastDown(KeyButton value) { m_lastDown = value; }
+ KeyModifierMask getSavedModifiers() const { return m_savedModifiers; }
+ void setSavedModifiers(KeyModifierMask value) { m_savedModifiers = value; }
+
+protected:
+ // KeyState overrides
+ virtual void getKeyMap(barrier::KeyMap& keyMap);
+ virtual void fakeKey(const Keystroke& keystroke);
+ virtual KeyModifierMask&
+ getActiveModifiersRValue();
+
+private:
+ typedef std::vector<HKL> GroupList;
+
+ // send ctrl+alt+del hotkey event on NT family
+ static void ctrlAltDelThread(void*);
+
+ bool getGroups(GroupList&) const;
+ void setWindowGroup(SInt32 group);
+
+ KeyID getIDForKey(barrier::KeyMap::KeyItem& item,
+ KeyButton button, UINT virtualKey,
+ PBYTE keyState, HKL hkl) const;
+
+ void addKeyEntry(barrier::KeyMap& keyMap, barrier::KeyMap::KeyItem& item);
+
+ void init();
+
+private:
+ // not implemented
+ MSWindowsKeyState(const MSWindowsKeyState&);
+ MSWindowsKeyState& operator=(const MSWindowsKeyState&);
+
+private:
+ typedef std::map<HKL, SInt32> GroupMap;
+ typedef std::map<KeyID, UINT> KeyToVKMap;
+
+ void* m_eventTarget;
+ MSWindowsDesks* m_desks;
+ HKL m_keyLayout;
+ UINT m_buttonToVK[512];
+ UINT m_buttonToNumpadVK[512];
+ KeyButton m_virtualKeyToButton[256];
+ KeyToVKMap m_keyToVKMap;
+ IEventQueue* m_events;
+
+ // the timer used to check for fixing key state
+ EventQueueTimer* m_fixTimer;
+
+ // the groups (keyboard layouts)
+ GroupList m_groups;
+ GroupMap m_groupMap;
+
+ // the last button that we generated a key down event for. this
+ // is zero if the last key event was a key up. we use this to
+ // synthesize key repeats since the low level keyboard hook can't
+ // tell us if an event is a key repeat.
+ KeyButton m_lastDown;
+
+ // modifier tracking
+ bool m_useSavedModifiers;
+ KeyModifierMask m_savedModifiers;
+ KeyModifierMask m_originalSavedModifiers;
+
+ // pointer to ToUnicodeEx. on win95 family this will be NULL.
+ typedef int (WINAPI *ToUnicodeEx_t)(UINT wVirtKey,
+ UINT wScanCode,
+ PBYTE lpKeyState,
+ LPWSTR pwszBuff,
+ int cchBuff,
+ UINT wFlags,
+ HKL dwhkl);
+ ToUnicodeEx_t m_ToUnicodeEx;
+
+ static const KeyID s_virtualKey[];
+};
diff --git a/src/lib/platform/MSWindowsScreen.cpp b/src/lib/platform/MSWindowsScreen.cpp
new file mode 100644
index 0000000..5246f96
--- /dev/null
+++ b/src/lib/platform/MSWindowsScreen.cpp
@@ -0,0 +1,1959 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2018 Debauchee Open Source Group
+ * 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 "platform/MSWindowsScreen.h"
+
+#include "platform/MSWindowsDropTarget.h"
+#include "client/Client.h"
+#include "platform/MSWindowsClipboard.h"
+#include "platform/MSWindowsDesks.h"
+#include "platform/MSWindowsEventQueueBuffer.h"
+#include "platform/MSWindowsKeyState.h"
+#include "platform/MSWindowsScreenSaver.h"
+#include "barrier/Clipboard.h"
+#include "barrier/KeyMap.h"
+#include "barrier/XScreen.h"
+#include "barrier/App.h"
+#include "barrier/ArgsBase.h"
+#include "barrier/ClientApp.h"
+#include "mt/Lock.h"
+#include "mt/Thread.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "arch/Arch.h"
+#include "base/FunctionJob.h"
+#include "base/Log.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+#include "base/TMethodJob.h"
+
+#include <string.h>
+#include <Shlobj.h>
+#include <comutil.h>
+#include <algorithm>
+
+//
+// add backwards compatible multihead support (and suppress bogus warning).
+// this isn't supported on MinGW yet AFAICT.
+//
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable: 4706) // assignment within conditional
+#define COMPILE_MULTIMON_STUBS
+#include <multimon.h>
+#pragma warning(pop)
+#endif
+
+// X button stuff
+#if !defined(WM_XBUTTONDOWN)
+#define WM_XBUTTONDOWN 0x020B
+#define WM_XBUTTONUP 0x020C
+#define WM_XBUTTONDBLCLK 0x020D
+#define WM_NCXBUTTONDOWN 0x00AB
+#define WM_NCXBUTTONUP 0x00AC
+#define WM_NCXBUTTONDBLCLK 0x00AD
+#define MOUSEEVENTF_XDOWN 0x0080
+#define MOUSEEVENTF_XUP 0x0100
+#define XBUTTON1 0x0001
+#define XBUTTON2 0x0002
+#endif
+#if !defined(VK_XBUTTON1)
+#define VK_XBUTTON1 0x05
+#define VK_XBUTTON2 0x06
+#endif
+
+// WM_POWERBROADCAST stuff
+#if !defined(PBT_APMRESUMEAUTOMATIC)
+#define PBT_APMRESUMEAUTOMATIC 0x0012
+#endif
+
+//
+// MSWindowsScreen
+//
+
+HINSTANCE MSWindowsScreen::s_windowInstance = NULL;
+MSWindowsScreen* MSWindowsScreen::s_screen = NULL;
+
+MSWindowsScreen::MSWindowsScreen(
+ bool isPrimary,
+ bool noHooks,
+ bool stopOnDeskSwitch,
+ IEventQueue* events) :
+ PlatformScreen(events),
+ m_isPrimary(isPrimary),
+ m_noHooks(noHooks),
+ m_isOnScreen(m_isPrimary),
+ m_class(0),
+ m_x(0), m_y(0),
+ m_w(0), m_h(0),
+ m_xCenter(0), m_yCenter(0),
+ m_multimon(false),
+ m_xCursor(0), m_yCursor(0),
+ m_sequenceNumber(0),
+ m_mark(0),
+ m_markReceived(0),
+ m_fixTimer(NULL),
+ m_keyLayout(NULL),
+ m_screensaver(NULL),
+ m_screensaverNotify(false),
+ m_screensaverActive(false),
+ m_window(NULL),
+ m_nextClipboardWindow(NULL),
+ m_ownClipboard(false),
+ m_desks(NULL),
+ m_keyState(NULL),
+ m_hasMouse(GetSystemMetrics(SM_MOUSEPRESENT) != 0),
+ m_showingMouse(false),
+ m_events(events),
+ m_dropWindow(NULL),
+ m_dropWindowSize(20)
+{
+ assert(s_windowInstance != NULL);
+ assert(s_screen == NULL);
+
+ s_screen = this;
+ try {
+ m_screensaver = new MSWindowsScreenSaver();
+ m_desks = new MSWindowsDesks(
+ m_isPrimary,
+ m_noHooks,
+ m_screensaver,
+ m_events,
+ new TMethodJob<MSWindowsScreen>(
+ this, &MSWindowsScreen::updateKeysCB),
+ stopOnDeskSwitch);
+ m_keyState = new MSWindowsKeyState(m_desks, getEventTarget(), m_events);
+
+ updateScreenShape();
+ m_class = createWindowClass();
+ m_window = createWindow(m_class, "Barrier");
+ forceShowCursor();
+ LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : ""));
+ LOG((CLOG_DEBUG "window is 0x%08x", m_window));
+
+ // SHGetFolderPath is deprecated in vista, but use it for xp support.
+ char desktopPath[MAX_PATH];
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath))) {
+ m_desktopPath = String(desktopPath);
+ LOG((CLOG_DEBUG "using desktop for drop target: %s", m_desktopPath.c_str()));
+ }
+ else {
+ LOG((CLOG_ERR "failed to get desktop path, no drop target available, error=%d", GetLastError()));
+ }
+
+ OleInitialize(0);
+ m_dropWindow = createDropWindow(m_class, "DropWindow");
+ m_dropTarget = new MSWindowsDropTarget();
+ RegisterDragDrop(m_dropWindow, m_dropTarget);
+ }
+ catch (...) {
+ delete m_keyState;
+ delete m_desks;
+ delete m_screensaver;
+ destroyWindow(m_window);
+ destroyClass(m_class);
+ s_screen = NULL;
+ throw;
+ }
+
+ // install event handlers
+ m_events->adoptHandler(Event::kSystem, m_events->getSystemTarget(),
+ new TMethodEventJob<MSWindowsScreen>(this,
+ &MSWindowsScreen::handleSystemEvent));
+
+ // install the platform event queue
+ m_events->adoptBuffer(new MSWindowsEventQueueBuffer(m_events));
+}
+
+MSWindowsScreen::~MSWindowsScreen()
+{
+ assert(s_screen != NULL);
+
+ disable();
+ m_events->adoptBuffer(NULL);
+ m_events->removeHandler(Event::kSystem, m_events->getSystemTarget());
+ delete m_keyState;
+ delete m_desks;
+ delete m_screensaver;
+ destroyWindow(m_window);
+ destroyClass(m_class);
+
+ RevokeDragDrop(m_dropWindow);
+ m_dropTarget->Release();
+ OleUninitialize();
+ destroyWindow(m_dropWindow);
+
+ s_screen = NULL;
+}
+
+void
+MSWindowsScreen::init(HINSTANCE windowInstance)
+{
+ assert(s_windowInstance == NULL);
+ assert(windowInstance != NULL);
+
+ s_windowInstance = windowInstance;
+}
+
+HINSTANCE
+MSWindowsScreen::getWindowInstance()
+{
+ return s_windowInstance;
+}
+
+void
+MSWindowsScreen::enable()
+{
+ assert(m_isOnScreen == m_isPrimary);
+
+ // we need to poll some things to fix them
+ m_fixTimer = m_events->newTimer(1.0, NULL);
+ m_events->adoptHandler(Event::kTimer, m_fixTimer,
+ new TMethodEventJob<MSWindowsScreen>(this,
+ &MSWindowsScreen::handleFixes));
+
+ // install our clipboard snooper
+ m_nextClipboardWindow = SetClipboardViewer(m_window);
+
+ // track the active desk and (re)install the hooks
+ m_desks->enable();
+
+ if (m_isPrimary) {
+ // set jump zones
+ m_hook.setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
+
+ // watch jump zones
+ m_hook.setMode(kHOOK_WATCH_JUMP_ZONE);
+ }
+ else {
+ // prevent the system from entering power saving modes. if
+ // it did we'd be forced to disconnect from the server and
+ // the server would not be able to wake us up.
+ ArchMiscWindows::addBusyState(ArchMiscWindows::kSYSTEM);
+ }
+}
+
+void
+MSWindowsScreen::disable()
+{
+ // stop tracking the active desk
+ m_desks->disable();
+
+ if (m_isPrimary) {
+ // disable hooks
+ m_hook.setMode(kHOOK_DISABLE);
+
+ // enable special key sequences on win95 family
+ enableSpecialKeys(true);
+ }
+ else {
+ // allow the system to enter power saving mode
+ ArchMiscWindows::removeBusyState(ArchMiscWindows::kSYSTEM |
+ ArchMiscWindows::kDISPLAY);
+ }
+
+ // tell key state
+ m_keyState->disable();
+
+ // stop snooping the clipboard
+ ChangeClipboardChain(m_window, m_nextClipboardWindow);
+ m_nextClipboardWindow = NULL;
+
+ // uninstall fix timer
+ if (m_fixTimer != NULL) {
+ m_events->removeHandler(Event::kTimer, m_fixTimer);
+ m_events->deleteTimer(m_fixTimer);
+ m_fixTimer = NULL;
+ }
+
+ m_isOnScreen = m_isPrimary;
+ forceShowCursor();
+}
+
+void
+MSWindowsScreen::enter()
+{
+ m_desks->enter();
+ if (m_isPrimary) {
+ // enable special key sequences on win95 family
+ enableSpecialKeys(true);
+
+ // watch jump zones
+ m_hook.setMode(kHOOK_WATCH_JUMP_ZONE);
+
+ // all messages prior to now are invalid
+ nextMark();
+
+ m_primaryKeyDownList.clear();
+ }
+ else {
+ // Entering a secondary screen. Ensure that no screensaver is active
+ // and that the screen is not in powersave mode.
+ ArchMiscWindows::wakeupDisplay();
+
+ if (m_screensaver != NULL && m_screensaverActive)
+ {
+ m_screensaver->deactivate();
+ m_screensaverActive = 0;
+ }
+ }
+
+ // now on screen
+ m_isOnScreen = true;
+ forceShowCursor();
+}
+
+bool
+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);
+
+ // tell the key mapper about the keyboard layout
+ m_keyState->setKeyLayout(m_keyLayout);
+
+ // tell desk that we're leaving and tell it the keyboard layout
+ m_desks->leave(m_keyLayout);
+
+ if (m_isPrimary) {
+
+ // warp to center
+ LOG((CLOG_DEBUG1 "warping cursor to center: %+d, %+d", m_xCenter, m_yCenter));
+ warpCursor(m_xCenter, m_yCenter);
+
+ // disable special key sequences on win95 family
+ enableSpecialKeys(false);
+
+ // all messages prior to now are invalid
+ nextMark();
+
+ // remember the modifier state. this is the modifier state
+ // reflected in the internal keyboard state.
+ m_keyState->saveModifiers();
+
+ m_hook.setMode(kHOOK_RELAY_EVENTS);
+
+ m_primaryKeyDownList.clear();
+ for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
+ if (m_keyState->isKeyDown(i)) {
+ m_primaryKeyDownList.push_back(i);
+ LOG((CLOG_DEBUG1 "key button %d is down before leaving to another screen", i));
+ }
+ }
+ }
+
+ // now off screen
+ m_isOnScreen = false;
+ forceShowCursor();
+
+ if (isDraggingStarted() && !m_isPrimary) {
+ m_sendDragThread = new Thread(
+ new TMethodJob<MSWindowsScreen>(
+ this,
+ &MSWindowsScreen::sendDragThread));
+ }
+
+ return true;
+}
+
+void
+MSWindowsScreen::sendDragThread(void*)
+{
+ String& draggingFilename = getDraggingFilename();
+ size_t size = draggingFilename.size();
+
+ if (draggingFilename.empty() == false) {
+ ClientApp& app = ClientApp::instance();
+ Client* client = app.getClientPtr();
+ UInt32 fileCount = 1;
+ LOG((CLOG_DEBUG "send dragging info to server: %s", draggingFilename.c_str()));
+ client->sendDragInfo(fileCount, draggingFilename, size);
+ LOG((CLOG_DEBUG "send dragging file to server"));
+ client->sendFileToServer(draggingFilename.c_str());
+ }
+
+ m_draggingStarted = false;
+}
+
+bool
+MSWindowsScreen::setClipboard(ClipboardID, const IClipboard* src)
+{
+ MSWindowsClipboard dst(m_window);
+ if (src != NULL) {
+ // save clipboard data
+ return Clipboard::copy(&dst, src);
+ }
+ else {
+ // assert clipboard ownership
+ if (!dst.open(0)) {
+ return false;
+ }
+ dst.empty();
+ dst.close();
+ return true;
+ }
+}
+
+void
+MSWindowsScreen::checkClipboards()
+{
+ // if we think we own the clipboard but we don't then somebody
+ // grabbed the clipboard on this screen without us knowing.
+ // tell the server that this screen grabbed the clipboard.
+ //
+ // this works around bugs in the clipboard viewer chain.
+ // sometimes NT will simply never send WM_DRAWCLIPBOARD
+ // messages for no apparent reason and rebooting fixes the
+ // problem. since we don't want a broken clipboard until the
+ // next reboot we do this double check. clipboard ownership
+ // won't be reflected on other screens until we leave but at
+ // least the clipboard itself will work.
+ if (m_ownClipboard && !MSWindowsClipboard::isOwnedByBarrier()) {
+ LOG((CLOG_DEBUG "clipboard changed: lost ownership and no notification received"));
+ m_ownClipboard = false;
+ sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
+ sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
+ }
+}
+
+void
+MSWindowsScreen::openScreensaver(bool notify)
+{
+ assert(m_screensaver != NULL);
+
+ m_screensaverNotify = notify;
+ if (m_screensaverNotify) {
+ m_desks->installScreensaverHooks(true);
+ }
+ else if (m_screensaver) {
+ m_screensaver->disable();
+ }
+}
+
+void
+MSWindowsScreen::closeScreensaver()
+{
+ if (m_screensaver != NULL) {
+ if (m_screensaverNotify) {
+ m_desks->installScreensaverHooks(false);
+ }
+ else {
+ m_screensaver->enable();
+ }
+ }
+ m_screensaverNotify = false;
+}
+
+void
+MSWindowsScreen::screensaver(bool activate)
+{
+ assert(m_screensaver != NULL);
+ if (m_screensaver==NULL) return;
+
+ if (activate) {
+ m_screensaver->activate();
+ }
+ else {
+ m_screensaver->deactivate();
+ }
+}
+
+void
+MSWindowsScreen::resetOptions()
+{
+ m_desks->resetOptions();
+}
+
+void
+MSWindowsScreen::setOptions(const OptionsList& options)
+{
+ m_desks->setOptions(options);
+}
+
+void
+MSWindowsScreen::setSequenceNumber(UInt32 seqNum)
+{
+ m_sequenceNumber = seqNum;
+}
+
+bool
+MSWindowsScreen::isPrimary() const
+{
+ return m_isPrimary;
+}
+
+void*
+MSWindowsScreen::getEventTarget() const
+{
+ return const_cast<MSWindowsScreen*>(this);
+}
+
+bool
+MSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const
+{
+ MSWindowsClipboard src(m_window);
+ Clipboard::copy(dst, &src);
+ return true;
+}
+
+void
+MSWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
+{
+ assert(m_class != 0);
+
+ x = m_x;
+ y = m_y;
+ w = m_w;
+ h = m_h;
+}
+
+void
+MSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
+{
+ m_desks->getCursorPos(x, y);
+}
+
+void
+MSWindowsScreen::reconfigure(UInt32 activeSides)
+{
+ assert(m_isPrimary);
+
+ LOG((CLOG_DEBUG "active sides: %x", activeSides));
+ m_hook.setSides(activeSides);
+}
+
+void
+MSWindowsScreen::warpCursor(SInt32 x, SInt32 y)
+{
+ // warp mouse
+ warpCursorNoFlush(x, y);
+
+ // remove all input events before and including warp
+ MSG msg;
+ while (PeekMessage(&msg, NULL, BARRIER_MSG_INPUT_FIRST,
+ BARRIER_MSG_INPUT_LAST, PM_REMOVE)) {
+ // do nothing
+ }
+
+ // save position to compute delta of next motion
+ saveMousePosition(x, y);
+}
+
+void MSWindowsScreen::saveMousePosition(SInt32 x, SInt32 y) {
+ m_xCursor = x;
+ m_yCursor = y;
+
+ LOG((CLOG_DEBUG5 "saved mouse position for next delta: %+d,%+d", x,y));
+}
+
+UInt32
+MSWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask)
+{
+ // only allow certain modifiers
+ if ((mask & ~(KeyModifierShift | KeyModifierControl |
+ KeyModifierAlt | KeyModifierSuper)) != 0) {
+ // this should be a warning, but this can confuse users,
+ // as this warning happens almost always.
+ LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
+ return 0;
+ }
+
+ // fail if no keys
+ if (key == kKeyNone && mask == 0) {
+ return 0;
+ }
+
+ // convert to win32
+ UINT modifiers = 0;
+ if ((mask & KeyModifierShift) != 0) {
+ modifiers |= MOD_SHIFT;
+ }
+ if ((mask & KeyModifierControl) != 0) {
+ modifiers |= MOD_CONTROL;
+ }
+ if ((mask & KeyModifierAlt) != 0) {
+ modifiers |= MOD_ALT;
+ }
+ if ((mask & KeyModifierSuper) != 0) {
+ modifiers |= MOD_WIN;
+ }
+ UINT vk = m_keyState->mapKeyToVirtualKey(key);
+ if (key != kKeyNone && vk == 0) {
+ // can't map key
+ // this should be a warning, but this can confuse users,
+ // as this warning happens almost always.
+ LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
+ return 0;
+ }
+
+ // choose hotkey id
+ UInt32 id;
+ if (!m_oldHotKeyIDs.empty()) {
+ id = m_oldHotKeyIDs.back();
+ m_oldHotKeyIDs.pop_back();
+ }
+ else {
+ //id = m_hotKeys.size() + 1;
+ id = (UInt32)m_hotKeys.size() + 1;
+ }
+
+ // if this hot key has modifiers only then we'll handle it specially
+ bool err;
+ if (key == kKeyNone) {
+ // check if already registered
+ err = (m_hotKeyToIDMap.count(HotKeyItem(vk, modifiers)) > 0);
+ }
+ else {
+ // register with OS
+ err = (RegisterHotKey(NULL, id, modifiers, vk) == 0);
+ }
+
+ if (!err) {
+ m_hotKeys.insert(std::make_pair(id, HotKeyItem(vk, modifiers)));
+ m_hotKeyToIDMap[HotKeyItem(vk, modifiers)] = id;
+ }
+ else {
+ m_oldHotKeyIDs.push_back(id);
+ m_hotKeys.erase(id);
+ LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask));
+ return 0;
+ }
+
+ LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask, id));
+ return id;
+}
+
+void
+MSWindowsScreen::unregisterHotKey(UInt32 id)
+{
+ // look up hotkey
+ HotKeyMap::iterator i = m_hotKeys.find(id);
+ if (i == m_hotKeys.end()) {
+ return;
+ }
+
+ // unregister with OS
+ bool err;
+ if (i->second.getVirtualKey() != 0) {
+ err = !UnregisterHotKey(NULL, id);
+ }
+ else {
+ err = false;
+ }
+ if (err) {
+ LOG((CLOG_WARN "failed to unregister hotkey id=%d", id));
+ }
+ else {
+ LOG((CLOG_DEBUG "unregistered hotkey id=%d", id));
+ }
+
+ // discard hot key from map and record old id for reuse
+ m_hotKeyToIDMap.erase(i->second);
+ m_hotKeys.erase(i);
+ m_oldHotKeyIDs.push_back(id);
+}
+
+void
+MSWindowsScreen::fakeInputBegin()
+{
+ assert(m_isPrimary);
+
+ if (!m_isOnScreen) {
+ m_keyState->useSavedModifiers(true);
+ }
+ m_desks->fakeInputBegin();
+}
+
+void
+MSWindowsScreen::fakeInputEnd()
+{
+ assert(m_isPrimary);
+
+ m_desks->fakeInputEnd();
+ if (!m_isOnScreen) {
+ m_keyState->useSavedModifiers(false);
+ }
+}
+
+SInt32
+MSWindowsScreen::getJumpZoneSize() const
+{
+ return 1;
+}
+
+bool
+MSWindowsScreen::isAnyMouseButtonDown(UInt32& buttonID) const
+{
+ static const char* buttonToName[] = {
+ "<invalid>",
+ "Left Button",
+ "Middle Button",
+ "Right Button",
+ "X Button 1",
+ "X Button 2"
+ };
+
+ for (UInt32 i = 1; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) {
+ if (m_buttons[i]) {
+ buttonID = i;
+ LOG((CLOG_DEBUG "locked by \"%s\"", buttonToName[i]));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+MSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
+{
+ x = m_xCenter;
+ y = m_yCenter;
+}
+
+void
+MSWindowsScreen::fakeMouseButton(ButtonID id, bool press)
+{
+ m_desks->fakeMouseButton(id, press);
+
+ if (id == kButtonLeft) {
+ if (press) {
+ m_buttons[kButtonLeft] = true;
+ }
+ else {
+ m_buttons[kButtonLeft] = false;
+ m_fakeDraggingStarted = false;
+ m_draggingStarted = false;
+ }
+ }
+}
+
+void
+MSWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y)
+{
+ m_desks->fakeMouseMove(x, y);
+ if (m_buttons[kButtonLeft]) {
+ m_draggingStarted = true;
+ }
+}
+
+void
+MSWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
+{
+ m_desks->fakeMouseRelativeMove(dx, dy);
+}
+
+void
+MSWindowsScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
+{
+ m_desks->fakeMouseWheel(xDelta, yDelta);
+}
+
+void
+MSWindowsScreen::updateKeys()
+{
+ m_desks->updateKeys();
+}
+
+void
+MSWindowsScreen::fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button)
+{
+ PlatformScreen::fakeKeyDown(id, mask, button);
+ updateForceShowCursor();
+}
+
+bool
+MSWindowsScreen::fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button)
+{
+ bool result = PlatformScreen::fakeKeyRepeat(id, mask, count, button);
+ updateForceShowCursor();
+ return result;
+}
+
+bool
+MSWindowsScreen::fakeKeyUp(KeyButton button)
+{
+ bool result = PlatformScreen::fakeKeyUp(button);
+ updateForceShowCursor();
+ return result;
+}
+
+void
+MSWindowsScreen::fakeAllKeysUp()
+{
+ PlatformScreen::fakeAllKeysUp();
+ updateForceShowCursor();
+}
+
+HCURSOR
+MSWindowsScreen::createBlankCursor() const
+{
+ // create a transparent cursor
+ int cw = GetSystemMetrics(SM_CXCURSOR);
+ int ch = GetSystemMetrics(SM_CYCURSOR);
+
+ UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)];
+ UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)];
+ memset(cursorAND, 0xff, ch * ((cw + 31) >> 2));
+ memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2));
+ HCURSOR c = CreateCursor(s_windowInstance, 0, 0, cw, ch, cursorAND, cursorXOR);
+ delete[] cursorXOR;
+ delete[] cursorAND;
+ return c;
+}
+
+void
+MSWindowsScreen::destroyCursor(HCURSOR cursor) const
+{
+ if (cursor != NULL) {
+ DestroyCursor(cursor);
+ }
+}
+
+ATOM
+MSWindowsScreen::createWindowClass() const
+{
+ WNDCLASSEX classInfo;
+ classInfo.cbSize = sizeof(classInfo);
+ classInfo.style = CS_DBLCLKS | CS_NOCLOSE;
+ classInfo.lpfnWndProc = &MSWindowsScreen::wndProc;
+ classInfo.cbClsExtra = 0;
+ classInfo.cbWndExtra = 0;
+ classInfo.hInstance = s_windowInstance;
+ classInfo.hIcon = NULL;
+ classInfo.hCursor = NULL;
+ classInfo.hbrBackground = NULL;
+ classInfo.lpszMenuName = NULL;
+ classInfo.lpszClassName = "Barrier";
+ classInfo.hIconSm = NULL;
+ return RegisterClassEx(&classInfo);
+}
+
+void
+MSWindowsScreen::destroyClass(ATOM windowClass) const
+{
+ if (windowClass != 0) {
+ UnregisterClass(MAKEINTATOM(windowClass), s_windowInstance);
+ }
+}
+
+HWND
+MSWindowsScreen::createWindow(ATOM windowClass, const char* name) const
+{
+ HWND window = CreateWindowEx(WS_EX_TOPMOST |
+ WS_EX_TRANSPARENT |
+ WS_EX_TOOLWINDOW,
+ MAKEINTATOM(windowClass),
+ name,
+ WS_POPUP,
+ 0, 0, 1, 1,
+ NULL, NULL,
+ s_windowInstance,
+ NULL);
+ if (window == NULL) {
+ LOG((CLOG_ERR "failed to create window: %d", GetLastError()));
+ throw XScreenOpenFailure();
+ }
+ return window;
+}
+
+HWND
+MSWindowsScreen::createDropWindow(ATOM windowClass, const char* name) const
+{
+ HWND window = CreateWindowEx(
+ WS_EX_TOPMOST |
+ WS_EX_TRANSPARENT |
+ WS_EX_ACCEPTFILES,
+ MAKEINTATOM(m_class),
+ name,
+ WS_POPUP,
+ 0, 0, m_dropWindowSize, m_dropWindowSize,
+ NULL, NULL,
+ s_windowInstance,
+ NULL);
+
+ if (window == NULL) {
+ LOG((CLOG_ERR "failed to create drop window: %d", GetLastError()));
+ throw XScreenOpenFailure();
+ }
+
+ return window;
+}
+
+void
+MSWindowsScreen::destroyWindow(HWND hwnd) const
+{
+ if (hwnd != NULL) {
+ DestroyWindow(hwnd);
+ }
+}
+
+void
+MSWindowsScreen::sendEvent(Event::Type type, void* data)
+{
+ m_events->addEvent(Event(type, getEventTarget(), data));
+}
+
+void
+MSWindowsScreen::sendClipboardEvent(Event::Type type, ClipboardID id)
+{
+ ClipboardInfo* info = (ClipboardInfo*)malloc(sizeof(ClipboardInfo));
+ if (info == NULL) {
+ LOG((CLOG_ERR "malloc failed on %s:%s", __FILE__, __LINE__ ));
+ return;
+ }
+ info->m_id = id;
+ info->m_sequenceNumber = m_sequenceNumber;
+ sendEvent(type, info);
+}
+
+void
+MSWindowsScreen::handleSystemEvent(const Event& event, void*)
+{
+ MSG* msg = static_cast<MSG*>(event.getData());
+ assert(msg != NULL);
+
+ if (ArchMiscWindows::processDialog(msg)) {
+ return;
+ }
+ if (onPreDispatch(msg->hwnd, msg->message, msg->wParam, msg->lParam)) {
+ return;
+ }
+ TranslateMessage(msg);
+ DispatchMessage(msg);
+}
+
+void
+MSWindowsScreen::updateButtons()
+{
+ int numButtons = GetSystemMetrics(SM_CMOUSEBUTTONS);
+ m_buttons[kButtonNone] = false;
+ m_buttons[kButtonLeft] = (GetKeyState(VK_LBUTTON) < 0);
+ m_buttons[kButtonRight] = (GetKeyState(VK_RBUTTON) < 0);
+ m_buttons[kButtonMiddle] = (GetKeyState(VK_MBUTTON) < 0);
+ m_buttons[kButtonExtra0 + 0] = (numButtons >= 4) &&
+ (GetKeyState(VK_XBUTTON1) < 0);
+ m_buttons[kButtonExtra0 + 1] = (numButtons >= 5) &&
+ (GetKeyState(VK_XBUTTON2) < 0);
+}
+
+IKeyState*
+MSWindowsScreen::getKeyState() const
+{
+ return m_keyState;
+}
+
+bool
+MSWindowsScreen::onPreDispatch(HWND hwnd,
+ UINT message, WPARAM wParam, LPARAM lParam)
+{
+ // handle event
+ switch (message) {
+ case BARRIER_MSG_SCREEN_SAVER:
+ return onScreensaver(wParam != 0);
+
+ case BARRIER_MSG_DEBUG:
+ LOG((CLOG_DEBUG1 "hook: 0x%08x 0x%08x", wParam, lParam));
+ return true;
+ }
+
+ if (m_isPrimary) {
+ return onPreDispatchPrimary(hwnd, message, wParam, lParam);
+ }
+
+ return false;
+}
+
+bool
+MSWindowsScreen::onPreDispatchPrimary(HWND,
+ UINT message, WPARAM wParam, LPARAM lParam)
+{
+ LOG((CLOG_DEBUG5 "handling pre-dispatch primary"));
+
+ // handle event
+ switch (message) {
+ case BARRIER_MSG_MARK:
+ return onMark(static_cast<UInt32>(wParam));
+
+ case BARRIER_MSG_KEY:
+ return onKey(wParam, lParam);
+
+ case BARRIER_MSG_MOUSE_BUTTON:
+ return onMouseButton(wParam, lParam);
+
+ case BARRIER_MSG_MOUSE_MOVE:
+ return onMouseMove(static_cast<SInt32>(wParam),
+ static_cast<SInt32>(lParam));
+
+ case BARRIER_MSG_MOUSE_WHEEL:
+ // XXX -- support x-axis scrolling
+ return onMouseWheel(0, static_cast<SInt32>(wParam));
+
+ case BARRIER_MSG_PRE_WARP:
+ {
+ // save position to compute delta of next motion
+ saveMousePosition(static_cast<SInt32>(wParam), static_cast<SInt32>(lParam));
+
+ // we warped the mouse. discard events until we find the
+ // matching post warp event. see warpCursorNoFlush() for
+ // where the events are sent. we discard the matching
+ // post warp event and can be sure we've skipped the warp
+ // event.
+ MSG msg;
+ do {
+ GetMessage(&msg, NULL, BARRIER_MSG_MOUSE_MOVE,
+ BARRIER_MSG_POST_WARP);
+ } while (msg.message != BARRIER_MSG_POST_WARP);
+ }
+ return true;
+
+ case BARRIER_MSG_POST_WARP:
+ LOG((CLOG_WARN "unmatched post warp"));
+ return true;
+
+ case WM_HOTKEY:
+ // we discard these messages. we'll catch the hot key in the
+ // regular key event handling, where we can detect both key
+ // press and release. we only register the hot key so no other
+ // app will act on the key combination.
+ break;
+ }
+
+ return false;
+}
+
+bool
+MSWindowsScreen::onEvent(HWND, UINT msg,
+ WPARAM wParam, LPARAM lParam, LRESULT* result)
+{
+ switch (msg) {
+ case WM_DRAWCLIPBOARD:
+ // first pass on the message
+ if (m_nextClipboardWindow != NULL) {
+ SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
+ }
+
+ // now handle the message
+ return onClipboardChange();
+
+ case WM_CHANGECBCHAIN:
+ if (m_nextClipboardWindow == (HWND)wParam) {
+ m_nextClipboardWindow = (HWND)lParam;
+ LOG((CLOG_DEBUG "clipboard chain: new next: 0x%08x", m_nextClipboardWindow));
+ }
+ else if (m_nextClipboardWindow != NULL) {
+ SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
+ }
+ return true;
+
+ case WM_DISPLAYCHANGE:
+ return onDisplayChange();
+
+ case WM_POWERBROADCAST:
+ switch (wParam) {
+ case PBT_APMRESUMEAUTOMATIC:
+ case PBT_APMRESUMECRITICAL:
+ case PBT_APMRESUMESUSPEND:
+ m_events->addEvent(Event(m_events->forIScreen().resume(),
+ getEventTarget(), NULL,
+ Event::kDeliverImmediately));
+ break;
+
+ case PBT_APMSUSPEND:
+ m_events->addEvent(Event(m_events->forIScreen().suspend(),
+ getEventTarget(), NULL,
+ Event::kDeliverImmediately));
+ break;
+ }
+ *result = TRUE;
+ return true;
+
+ case WM_DEVICECHANGE:
+ forceShowCursor();
+ break;
+
+ case WM_SETTINGCHANGE:
+ if (wParam == SPI_SETMOUSEKEYS) {
+ forceShowCursor();
+ }
+ break;
+ }
+
+ return false;
+}
+
+bool
+MSWindowsScreen::onMark(UInt32 mark)
+{
+ m_markReceived = mark;
+ return true;
+}
+
+bool
+MSWindowsScreen::onKey(WPARAM wParam, LPARAM lParam)
+{
+ static const KeyModifierMask s_ctrlAlt =
+ KeyModifierControl | KeyModifierAlt;
+
+ LOG((CLOG_DEBUG1 "event: Key char=%d, vk=0x%02x, nagr=%d, lParam=0x%08x", (wParam & 0xff00u) >> 8, wParam & 0xffu, (wParam & 0x10000u) ? 1 : 0, lParam));
+
+ // get event info
+ KeyButton button = (KeyButton)((lParam & 0x01ff0000) >> 16);
+ bool down = ((lParam & 0x80000000u) == 0x00000000u);
+ bool wasDown = isKeyDown(button);
+ KeyModifierMask oldState = pollActiveModifiers();
+
+ // check for autorepeat
+ if (m_keyState->testAutoRepeat(down, (lParam & 0x40000000u), button)) {
+ lParam |= 0x40000000u;
+ }
+
+ // if the button is zero then guess what the button should be.
+ // these are badly synthesized key events and logitech software
+ // that maps mouse buttons to keys is known to do this.
+ // alternatively, we could just throw these events out.
+ if (button == 0) {
+ button = m_keyState->virtualKeyToButton(wParam & 0xffu);
+ if (button == 0) {
+ return true;
+ }
+ wasDown = isKeyDown(button);
+ }
+
+ // record keyboard state
+ m_keyState->onKey(button, down, oldState);
+
+ if (!down && m_isPrimary && !m_isOnScreen) {
+ PrimaryKeyDownList::iterator find = std::find(m_primaryKeyDownList.begin(), m_primaryKeyDownList.end(), button);
+ if (find != m_primaryKeyDownList.end()) {
+ LOG((CLOG_DEBUG1 "release key button %d on primary", *find));
+ m_hook.setMode(kHOOK_WATCH_JUMP_ZONE);
+ fakeLocalKey(*find, false);
+ m_primaryKeyDownList.erase(find);
+ m_hook.setMode(kHOOK_RELAY_EVENTS);
+ return true;
+ }
+ }
+
+ // windows doesn't tell us the modifier key state on mouse or key
+ // events so we have to figure it out. most apps would use
+ // GetKeyState() or even GetAsyncKeyState() for that but we can't
+ // because our hook doesn't pass on key events for several modifiers.
+ // it can't otherwise the system would interpret them normally on
+ // the primary screen even when on a secondary screen. so tapping
+ // alt would activate menus and tapping the windows key would open
+ // the start menu. if you don't pass those events on in the hook
+ // then GetKeyState() understandably doesn't reflect the effect of
+ // the event. curiously, neither does GetAsyncKeyState(), which is
+ // surprising.
+ //
+ // so anyway, we have to track the modifier state ourselves for
+ // at least those modifiers we don't pass on. pollActiveModifiers()
+ // does that but we have to update the keyboard state before calling
+ // pollActiveModifiers() to get the right answer. but the only way
+ // to set the modifier state or to set the up/down state of a key
+ // is via onKey(). so we have to call onKey() twice.
+ KeyModifierMask state = pollActiveModifiers();
+ m_keyState->onKey(button, down, state);
+
+ // check for hot keys
+ if (oldState != state) {
+ // modifier key was pressed/released
+ if (onHotKey(0, lParam)) {
+ return true;
+ }
+ }
+ else {
+ // non-modifier was pressed/released
+ if (onHotKey(wParam, lParam)) {
+ return true;
+ }
+ }
+
+ // stop sending modifier keys over and over again
+ if (isModifierRepeat(oldState, state, wParam)) {
+ return true;
+ }
+
+ // ignore message if posted prior to last mark change
+ if (!ignore()) {
+ // check for ctrl+alt+del. we do not want to pass that to the
+ // client. the user can use ctrl+alt+pause to emulate it.
+ UINT virtKey = (wParam & 0xffu);
+ if (virtKey == VK_DELETE && (state & s_ctrlAlt) == s_ctrlAlt) {
+ LOG((CLOG_DEBUG "discard ctrl+alt+del"));
+ return true;
+ }
+
+ // check for ctrl+alt+del emulation
+ if ((virtKey == VK_PAUSE || virtKey == VK_CANCEL) &&
+ (state & s_ctrlAlt) == s_ctrlAlt) {
+ LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
+ // switch wParam and lParam to be as if VK_DELETE was
+ // pressed or released. when mapping the key we require that
+ // we not use AltGr (the 0x10000 flag in wParam) and we not
+ // use the keypad delete key (the 0x01000000 flag in lParam).
+ wParam = VK_DELETE | 0x00010000u;
+ lParam &= 0xfe000000;
+ lParam |= m_keyState->virtualKeyToButton(wParam & 0xffu) << 16;
+ lParam |= 0x01000001;
+ }
+
+ // process key
+ KeyModifierMask mask;
+ KeyID key = m_keyState->mapKeyFromEvent(wParam, lParam, &mask);
+ button = static_cast<KeyButton>((lParam & 0x01ff0000u) >> 16);
+ if (key != kKeyNone) {
+ // do it
+ m_keyState->sendKeyEvent(getEventTarget(),
+ ((lParam & 0x80000000u) == 0),
+ ((lParam & 0x40000000u) != 0),
+ key, mask, (SInt32)(lParam & 0xffff), button);
+ }
+ else {
+ LOG((CLOG_DEBUG1 "cannot map key"));
+ }
+ }
+
+ return true;
+}
+
+bool
+MSWindowsScreen::onHotKey(WPARAM wParam, LPARAM lParam)
+{
+ // get the key info
+ KeyModifierMask state = getActiveModifiers();
+ UINT virtKey = (wParam & 0xffu);
+ UINT modifiers = 0;
+ if ((state & KeyModifierShift) != 0) {
+ modifiers |= MOD_SHIFT;
+ }
+ if ((state & KeyModifierControl) != 0) {
+ modifiers |= MOD_CONTROL;
+ }
+ if ((state & KeyModifierAlt) != 0) {
+ modifiers |= MOD_ALT;
+ }
+ if ((state & KeyModifierSuper) != 0) {
+ modifiers |= MOD_WIN;
+ }
+
+ // find the hot key id
+ HotKeyToIDMap::const_iterator i =
+ m_hotKeyToIDMap.find(HotKeyItem(virtKey, modifiers));
+ if (i == m_hotKeyToIDMap.end()) {
+ return false;
+ }
+
+ // find what kind of event
+ Event::Type type;
+ if ((lParam & 0x80000000u) == 0u) {
+ if ((lParam & 0x40000000u) != 0u) {
+ // ignore key repeats but it counts as a hot key
+ return true;
+ }
+ type = m_events->forIPrimaryScreen().hotKeyDown();
+ }
+ else {
+ type = m_events->forIPrimaryScreen().hotKeyUp();
+ }
+
+ // generate event
+ m_events->addEvent(Event(type, getEventTarget(),
+ HotKeyInfo::alloc(i->second)));
+
+ return true;
+}
+
+bool
+MSWindowsScreen::onMouseButton(WPARAM wParam, LPARAM lParam)
+{
+ // get which button
+ bool pressed = mapPressFromEvent(wParam, lParam);
+ ButtonID button = mapButtonFromEvent(wParam, lParam);
+
+ // keep our shadow key state up to date
+ if (button >= kButtonLeft && button <= kButtonExtra0 + 1) {
+ if (pressed) {
+ m_buttons[button] = true;
+ if (button == kButtonLeft) {
+ m_draggingFilename.clear();
+ LOG((CLOG_DEBUG2 "dragging filename is cleared"));
+ }
+ }
+ else {
+ m_buttons[button] = false;
+ if (m_draggingStarted && button == kButtonLeft) {
+ m_draggingStarted = false;
+ }
+ }
+ }
+
+ // ignore message if posted prior to last mark change
+ if (!ignore()) {
+ KeyModifierMask mask = m_keyState->getActiveModifiers();
+ if (pressed) {
+ LOG((CLOG_DEBUG1 "event: button press button=%d", button));
+ if (button != kButtonNone) {
+ sendEvent(m_events->forIPrimaryScreen().buttonDown(),
+ ButtonInfo::alloc(button, mask));
+ }
+ }
+ else {
+ LOG((CLOG_DEBUG1 "event: button release button=%d", button));
+ if (button != kButtonNone) {
+ sendEvent(m_events->forIPrimaryScreen().buttonUp(),
+ ButtonInfo::alloc(button, mask));
+ }
+ }
+ }
+
+ return true;
+}
+
+// here's how mouse movements are sent across the network to a client:
+// 1. barrier checks the mouse position on server screen
+// 2. records the delta (current x,y minus last x,y)
+// 3. records the current x,y as "last" (so we can calc delta next time)
+// 4. on the server, puts the cursor back to the center of the screen
+// - remember the cursor is hidden on the server at this point
+// - this actually records the current x,y as "last" a second time (it seems)
+// 5. sends the delta movement to the client (could be +1,+1 or -1,+4 for example)
+bool
+MSWindowsScreen::onMouseMove(SInt32 mx, SInt32 my)
+{
+ // compute motion delta (relative to the last known
+ // mouse position)
+ SInt32 x = mx - m_xCursor;
+ SInt32 y = my - m_yCursor;
+
+ LOG((CLOG_DEBUG3
+ "mouse move - motion delta: %+d=(%+d - %+d),%+d=(%+d - %+d)",
+ x, mx, m_xCursor, y, my, m_yCursor));
+
+ // ignore if the mouse didn't move or if message posted prior
+ // to last mark change.
+ if (ignore() || (x == 0 && y == 0)) {
+ return true;
+ }
+
+ // save position to compute delta of next motion
+ saveMousePosition(mx, my);
+
+ if (m_isOnScreen) {
+
+ // motion on primary screen
+ sendEvent(
+ m_events->forIPrimaryScreen().motionOnPrimary(),
+ MotionInfo::alloc(m_xCursor, m_yCursor));
+
+ if (m_buttons[kButtonLeft] == true && m_draggingStarted == false) {
+ m_draggingStarted = true;
+ }
+ }
+ else
+ {
+ // the motion is on the secondary screen, so we warp mouse back to
+ // center on the server screen. if we don't do this, then the mouse
+ // will always try to return to the original entry point on the
+ // secondary screen.
+ LOG((CLOG_DEBUG5 "warping server cursor to center: %+d,%+d", m_xCenter, m_yCenter));
+ warpCursorNoFlush(m_xCenter, m_yCenter);
+
+ // examine the motion. if it's about the distance
+ // from the center of the screen to an edge then
+ // it's probably a bogus motion that we want to
+ // ignore (see warpCursorNoFlush() for a further
+ // description).
+ static SInt32 bogusZoneSize = 10;
+ if (-x + bogusZoneSize > m_xCenter - m_x ||
+ x + bogusZoneSize > m_x + m_w - m_xCenter ||
+ -y + bogusZoneSize > m_yCenter - m_y ||
+ y + bogusZoneSize > m_y + m_h - m_yCenter) {
+
+ LOG((CLOG_DEBUG "dropped bogus delta motion: %+d,%+d", x, y));
+ }
+ else {
+ // send motion
+ sendEvent(m_events->forIPrimaryScreen().motionOnSecondary(), MotionInfo::alloc(x, y));
+ }
+ }
+
+ return true;
+}
+
+bool
+MSWindowsScreen::onMouseWheel(SInt32 xDelta, SInt32 yDelta)
+{
+ // ignore message if posted prior to last mark change
+ if (!ignore()) {
+ LOG((CLOG_DEBUG1 "event: button wheel delta=%+d,%+d", xDelta, yDelta));
+ sendEvent(m_events->forIPrimaryScreen().wheel(), WheelInfo::alloc(xDelta, yDelta));
+ }
+ return true;
+}
+
+bool
+MSWindowsScreen::onScreensaver(bool activated)
+{
+ // ignore this message if there are any other screen saver
+ // messages already in the queue. this is important because
+ // our checkStarted() function has a deliberate delay, so it
+ // can't respond to events at full CPU speed and will fall
+ // behind if a lot of screen saver events are generated.
+ // that can easily happen because windows will continually
+ // send SC_SCREENSAVE until the screen saver starts, even if
+ // the screen saver is disabled!
+ MSG msg;
+ if (PeekMessage(&msg, NULL, BARRIER_MSG_SCREEN_SAVER,
+ BARRIER_MSG_SCREEN_SAVER, PM_NOREMOVE)) {
+ return true;
+ }
+
+ if (activated) {
+ if (!m_screensaverActive &&
+ m_screensaver->checkStarted(BARRIER_MSG_SCREEN_SAVER, FALSE, 0)) {
+ m_screensaverActive = true;
+ sendEvent(m_events->forIPrimaryScreen().screensaverActivated());
+
+ // enable display power down
+ ArchMiscWindows::removeBusyState(ArchMiscWindows::kDISPLAY);
+ }
+ }
+ else {
+ if (m_screensaverActive) {
+ m_screensaverActive = false;
+ sendEvent(m_events->forIPrimaryScreen().screensaverDeactivated());
+
+ // disable display power down
+ ArchMiscWindows::addBusyState(ArchMiscWindows::kDISPLAY);
+ }
+ }
+
+ return true;
+}
+
+bool
+MSWindowsScreen::onDisplayChange()
+{
+ // screen resolution may have changed. save old shape.
+ SInt32 xOld = m_x, yOld = m_y, wOld = m_w, hOld = m_h;
+
+ // update shape
+ updateScreenShape();
+
+ // do nothing if resolution hasn't changed
+ if (xOld != m_x || yOld != m_y || wOld != m_w || hOld != m_h) {
+ if (m_isPrimary) {
+ // warp mouse to center if off screen
+ if (!m_isOnScreen) {
+
+ LOG((CLOG_DEBUG1 "warping cursor to center: %+d, %+d", m_xCenter, m_yCenter));
+ warpCursor(m_xCenter, m_yCenter);
+ }
+
+ // tell hook about resize if on screen
+ else {
+ m_hook.setZone(m_x, m_y, m_w, m_h, getJumpZoneSize());
+ }
+ }
+
+ // send new screen info
+ sendEvent(m_events->forIScreen().shapeChanged());
+
+ LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : ""));
+ }
+
+ return true;
+}
+
+bool
+MSWindowsScreen::onClipboardChange()
+{
+ // now notify client that somebody changed the clipboard (unless
+ // we're the owner).
+ if (!MSWindowsClipboard::isOwnedByBarrier()) {
+ if (m_ownClipboard) {
+ LOG((CLOG_DEBUG "clipboard changed: lost ownership"));
+ m_ownClipboard = false;
+ sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
+ sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
+ }
+ }
+ else if (!m_ownClipboard) {
+ LOG((CLOG_DEBUG "clipboard changed: barrier owned"));
+ m_ownClipboard = true;
+ }
+
+ return true;
+}
+
+void
+MSWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y)
+{
+ // send an event that we can recognize before the mouse warp
+ PostThreadMessage(GetCurrentThreadId(), BARRIER_MSG_PRE_WARP, x, y);
+
+ // warp mouse. hopefully this inserts a mouse motion event
+ // between the previous message and the following message.
+ SetCursorPos(x, y);
+
+ // check to see if the mouse pos was set correctly
+ POINT cursorPos;
+ GetCursorPos(&cursorPos);
+
+ // there is a bug or round error in SetCursorPos and GetCursorPos on
+ // a high DPI setting. The check here is for Vista/7 login screen.
+ // since this feature is mainly for client, so only check on client.
+ if (!isPrimary()) {
+ if ((cursorPos.x != x) && (cursorPos.y != y)) {
+ LOG((CLOG_DEBUG "SetCursorPos did not work; using fakeMouseMove instead"));
+ LOG((CLOG_DEBUG "cursor pos %d, %d expected pos %d, %d", cursorPos.x, cursorPos.y, x, y));
+ // when at Vista/7 login screen, SetCursorPos does not work (which could be
+ // an MS security feature). instead we can use fakeMouseMove, which calls
+ // mouse_event.
+ // IMPORTANT: as of implementing this function, it has an annoying side
+ // effect; instead of the mouse returning to the correct exit point, it
+ // returns to the center of the screen. this could have something to do with
+ // the center screen warping technique used (see comments for onMouseMove
+ // definition).
+ fakeMouseMove(x, y);
+ }
+ }
+
+ // yield the CPU. there's a race condition when warping:
+ // a hardware mouse event occurs
+ // the mouse hook is not called because that process doesn't have the CPU
+ // we send PRE_WARP, SetCursorPos(), send POST_WARP
+ // we process all of those events and update m_x, m_y
+ // we finish our time slice
+ // the hook is called
+ // the hook sends us a mouse event from the pre-warp position
+ // we get the CPU
+ // we compute a bogus warp
+ // we need the hook to process all mouse events that occur
+ // before we warp before we do the warp but i'm not sure how
+ // to guarantee that. yielding the CPU here may reduce the
+ // chance of undesired behavior. we'll also check for very
+ // large motions that look suspiciously like about half width
+ // or height of the screen.
+ ARCH->sleep(0.0);
+
+ // send an event that we can recognize after the mouse warp
+ PostThreadMessage(GetCurrentThreadId(), BARRIER_MSG_POST_WARP, 0, 0);
+}
+
+void
+MSWindowsScreen::nextMark()
+{
+ // next mark
+ ++m_mark;
+
+ // mark point in message queue where the mark was changed
+ PostThreadMessage(GetCurrentThreadId(), BARRIER_MSG_MARK, m_mark, 0);
+}
+
+bool
+MSWindowsScreen::ignore() const
+{
+ return (m_mark != m_markReceived);
+}
+
+void
+MSWindowsScreen::updateScreenShape()
+{
+ // get shape and center
+ m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+ m_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ m_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1;
+ m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1;
+
+ // check for multiple monitors
+ m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) ||
+ m_h != GetSystemMetrics(SM_CYSCREEN));
+
+ // tell the desks
+ m_desks->setShape(m_x, m_y, m_w, m_h, m_xCenter, m_yCenter, m_multimon);
+}
+
+void
+MSWindowsScreen::handleFixes(const Event&, void*)
+{
+ // fix clipboard chain
+ fixClipboardViewer();
+
+ // update keys if keyboard layouts have changed
+ if (m_keyState->didGroupsChange()) {
+ updateKeys();
+ }
+}
+
+void
+MSWindowsScreen::fixClipboardViewer()
+{
+ // XXX -- disable this code for now. somehow it can cause an infinite
+ // recursion in the WM_DRAWCLIPBOARD handler. either we're sending
+ // the message to our own window or some window farther down the chain
+ // forwards the message to our window or a window farther up the chain.
+ // i'm not sure how that could happen. the m_nextClipboardWindow = NULL
+ // was not in the code that infinite loops and may fix the bug but i
+ // doubt it.
+/*
+ ChangeClipboardChain(m_window, m_nextClipboardWindow);
+ m_nextClipboardWindow = NULL;
+ m_nextClipboardWindow = SetClipboardViewer(m_window);
+*/
+}
+
+void
+MSWindowsScreen::enableSpecialKeys(bool enable) const
+{
+}
+
+ButtonID
+MSWindowsScreen::mapButtonFromEvent(WPARAM msg, LPARAM button) const
+{
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ case WM_LBUTTONUP:
+ case WM_NCLBUTTONDOWN:
+ case WM_NCLBUTTONDBLCLK:
+ case WM_NCLBUTTONUP:
+ return kButtonLeft;
+
+ case WM_MBUTTONDOWN:
+ case WM_MBUTTONDBLCLK:
+ case WM_MBUTTONUP:
+ case WM_NCMBUTTONDOWN:
+ case WM_NCMBUTTONDBLCLK:
+ case WM_NCMBUTTONUP:
+ return kButtonMiddle;
+
+ case WM_RBUTTONDOWN:
+ case WM_RBUTTONDBLCLK:
+ case WM_RBUTTONUP:
+ case WM_NCRBUTTONDOWN:
+ case WM_NCRBUTTONDBLCLK:
+ case WM_NCRBUTTONUP:
+ return kButtonRight;
+
+ case WM_XBUTTONDOWN:
+ case WM_XBUTTONDBLCLK:
+ case WM_XBUTTONUP:
+ case WM_NCXBUTTONDOWN:
+ case WM_NCXBUTTONDBLCLK:
+ case WM_NCXBUTTONUP:
+ switch (button) {
+ case XBUTTON1:
+ if (GetSystemMetrics(SM_CMOUSEBUTTONS) >= 4) {
+ return kButtonExtra0 + 0;
+ }
+ break;
+
+ case XBUTTON2:
+ if (GetSystemMetrics(SM_CMOUSEBUTTONS) >= 5) {
+ return kButtonExtra0 + 1;
+ }
+ break;
+ }
+ return kButtonNone;
+
+ default:
+ return kButtonNone;
+ }
+}
+
+bool
+MSWindowsScreen::mapPressFromEvent(WPARAM msg, LPARAM) const
+{
+ switch (msg) {
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_XBUTTONDOWN:
+ case WM_LBUTTONDBLCLK:
+ case WM_MBUTTONDBLCLK:
+ case WM_RBUTTONDBLCLK:
+ case WM_XBUTTONDBLCLK:
+ case WM_NCLBUTTONDOWN:
+ case WM_NCMBUTTONDOWN:
+ case WM_NCRBUTTONDOWN:
+ case WM_NCXBUTTONDOWN:
+ case WM_NCLBUTTONDBLCLK:
+ case WM_NCMBUTTONDBLCLK:
+ case WM_NCRBUTTONDBLCLK:
+ case WM_NCXBUTTONDBLCLK:
+ return true;
+
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_XBUTTONUP:
+ case WM_NCLBUTTONUP:
+ case WM_NCMBUTTONUP:
+ case WM_NCRBUTTONUP:
+ case WM_NCXBUTTONUP:
+ return false;
+
+ default:
+ return false;
+ }
+}
+
+void
+MSWindowsScreen::updateKeysCB(void*)
+{
+ // record which keys we think are down
+ bool down[IKeyState::kNumButtons];
+ bool sendFixes = (isPrimary() && !m_isOnScreen);
+ if (sendFixes) {
+ for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
+ down[i] = m_keyState->isKeyDown(i);
+ }
+ }
+
+ // update layouts if necessary
+ if (m_keyState->didGroupsChange()) {
+ PlatformScreen::updateKeyMap();
+ }
+
+ // now update the keyboard state
+ PlatformScreen::updateKeyState();
+
+ // now see which keys we thought were down but now think are up.
+ // send key releases for these keys to the active client.
+ if (sendFixes) {
+ KeyModifierMask mask = pollActiveModifiers();
+ for (KeyButton i = 0; i < IKeyState::kNumButtons; ++i) {
+ if (down[i] && !m_keyState->isKeyDown(i)) {
+ m_keyState->sendKeyEvent(getEventTarget(),
+ false, false, kKeyNone, mask, 1, i);
+ }
+ }
+ }
+}
+
+void
+MSWindowsScreen::forceShowCursor()
+{
+ // check for mouse
+ m_hasMouse = (GetSystemMetrics(SM_MOUSEPRESENT) != 0);
+
+ // decide if we should show the mouse
+ bool showMouse = (!m_hasMouse && !m_isPrimary && m_isOnScreen);
+
+ // show/hide the mouse
+ if (showMouse != m_showingMouse) {
+ if (showMouse) {
+ m_oldMouseKeys.cbSize = sizeof(m_oldMouseKeys);
+ m_gotOldMouseKeys =
+ (SystemParametersInfo(SPI_GETMOUSEKEYS,
+ m_oldMouseKeys.cbSize, &m_oldMouseKeys, 0) != 0);
+ if (m_gotOldMouseKeys) {
+ m_mouseKeys = m_oldMouseKeys;
+ m_showingMouse = true;
+ updateForceShowCursor();
+ }
+ }
+ else {
+ if (m_gotOldMouseKeys) {
+ SystemParametersInfo(SPI_SETMOUSEKEYS,
+ m_oldMouseKeys.cbSize,
+ &m_oldMouseKeys, SPIF_SENDCHANGE);
+ m_showingMouse = false;
+ }
+ }
+ }
+}
+
+void
+MSWindowsScreen::updateForceShowCursor()
+{
+ DWORD oldFlags = m_mouseKeys.dwFlags;
+
+ // turn on MouseKeys
+ m_mouseKeys.dwFlags = MKF_AVAILABLE | MKF_MOUSEKEYSON;
+
+ // make sure MouseKeys is active in whatever state the NumLock is
+ // not currently in.
+ if ((m_keyState->getActiveModifiers() & KeyModifierNumLock) != 0) {
+ m_mouseKeys.dwFlags |= MKF_REPLACENUMBERS;
+ }
+
+ // update MouseKeys
+ if (oldFlags != m_mouseKeys.dwFlags) {
+ SystemParametersInfo(SPI_SETMOUSEKEYS,
+ m_mouseKeys.cbSize, &m_mouseKeys, SPIF_SENDCHANGE);
+ }
+}
+
+LRESULT CALLBACK
+MSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ assert(s_screen != NULL);
+
+ LRESULT result = 0;
+ if (!s_screen->onEvent(hwnd, msg, wParam, lParam, &result)) {
+ result = DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+
+ return result;
+}
+
+void
+MSWindowsScreen::fakeLocalKey(KeyButton button, bool press) const
+{
+ INPUT input;
+ input.type = INPUT_KEYBOARD;
+ input.ki.wVk = m_keyState->mapButtonToVirtualKey(button);
+ DWORD pressFlag = press ? KEYEVENTF_EXTENDEDKEY : KEYEVENTF_KEYUP;
+ input.ki.dwFlags = pressFlag;
+ input.ki.time = 0;
+ input.ki.dwExtraInfo = 0;
+ SendInput(1,&input,sizeof(input));
+}
+
+//
+// MSWindowsScreen::HotKeyItem
+//
+
+MSWindowsScreen::HotKeyItem::HotKeyItem(UINT keycode, UINT mask) :
+ m_keycode(keycode),
+ m_mask(mask)
+{
+ // do nothing
+}
+
+UINT
+MSWindowsScreen::HotKeyItem::getVirtualKey() const
+{
+ return m_keycode;
+}
+
+bool
+MSWindowsScreen::HotKeyItem::operator<(const HotKeyItem& x) const
+{
+ return (m_keycode < x.m_keycode ||
+ (m_keycode == x.m_keycode && m_mask < x.m_mask));
+}
+
+void
+MSWindowsScreen::fakeDraggingFiles(DragFileList fileList)
+{
+ // possible design flaw: this function stops a "not implemented"
+ // exception from being thrown.
+}
+
+String&
+MSWindowsScreen::getDraggingFilename()
+{
+ if (m_draggingStarted) {
+ m_dropTarget->clearDraggingFilename();
+ m_draggingFilename.clear();
+
+ int halfSize = m_dropWindowSize / 2;
+
+ SInt32 xPos = m_isPrimary ? m_xCursor : m_xCenter;
+ SInt32 yPos = m_isPrimary ? m_yCursor : m_yCenter;
+ xPos = (xPos - halfSize) < 0 ? 0 : xPos - halfSize;
+ yPos = (yPos - halfSize) < 0 ? 0 : yPos - halfSize;
+ SetWindowPos(
+ m_dropWindow,
+ HWND_TOPMOST,
+ xPos,
+ yPos,
+ m_dropWindowSize,
+ m_dropWindowSize,
+ SWP_SHOWWINDOW);
+
+ // TODO: fake these keys properly
+ fakeKeyDown(kKeyEscape, 8192, 1);
+ fakeKeyUp(1);
+ fakeMouseButton(kButtonLeft, false);
+
+ String filename;
+ DOUBLE timeout = ARCH->time() + .5f;
+ while (ARCH->time() < timeout) {
+ ARCH->sleep(.05f);
+ filename = m_dropTarget->getDraggingFilename();
+ if (!filename.empty()) {
+ break;
+ }
+ }
+
+ ShowWindow(m_dropWindow, SW_HIDE);
+
+ if (!filename.empty()) {
+ if (DragInformation::isFileValid(filename)) {
+ m_draggingFilename = filename;
+ }
+ else {
+ LOG((CLOG_DEBUG "drag file name is invalid: %s", filename.c_str()));
+ }
+ }
+
+ if (m_draggingFilename.empty()) {
+ LOG((CLOG_DEBUG "failed to get drag file name from OLE"));
+ }
+ }
+
+ return m_draggingFilename;
+}
+
+const String&
+MSWindowsScreen::getDropTarget() const
+{
+ return m_desktopPath;
+}
+
+bool
+MSWindowsScreen::isModifierRepeat(KeyModifierMask oldState, KeyModifierMask state, WPARAM wParam) const
+{
+ bool result = false;
+
+ if (oldState == state && state != 0) {
+ UINT virtKey = (wParam & 0xffu);
+ if ((state & KeyModifierShift) != 0
+ && (virtKey == VK_LSHIFT || virtKey == VK_RSHIFT)) {
+ result = true;
+ }
+ if ((state & KeyModifierControl) != 0
+ && (virtKey == VK_LCONTROL || virtKey == VK_RCONTROL)) {
+ result = true;
+ }
+ if ((state & KeyModifierAlt) != 0
+ && (virtKey == VK_LMENU || virtKey == VK_RMENU)) {
+ result = true;
+ }
+ if ((state & KeyModifierSuper) != 0
+ && (virtKey == VK_LWIN || virtKey == VK_RWIN)) {
+ result = true;
+ }
+ }
+
+ return result;
+}
diff --git a/src/lib/platform/MSWindowsScreen.h b/src/lib/platform/MSWindowsScreen.h
new file mode 100644
index 0000000..4245d6c
--- /dev/null
+++ b/src/lib/platform/MSWindowsScreen.h
@@ -0,0 +1,346 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2018 Debauchee Open Source Group
+ * 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 "platform/MSWindowsHook.h"
+#include "barrier/PlatformScreen.h"
+#include "barrier/DragInformation.h"
+#include "platform/synwinhk.h"
+#include "mt/CondVar.h"
+#include "mt/Mutex.h"
+#include "base/String.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+class EventQueueTimer;
+class MSWindowsDesks;
+class MSWindowsKeyState;
+class MSWindowsScreenSaver;
+class Thread;
+class MSWindowsDropTarget;
+
+//! Implementation of IPlatformScreen for Microsoft Windows
+class MSWindowsScreen : public PlatformScreen {
+public:
+ MSWindowsScreen(
+ bool isPrimary,
+ bool noHooks,
+ bool stopOnDeskSwitch,
+ IEventQueue* events);
+ virtual ~MSWindowsScreen();
+
+ //! @name manipulators
+ //@{
+
+ //! Initialize
+ /*!
+ Saves the application's HINSTANCE. This \b must be called by
+ WinMain with the HINSTANCE it was passed.
+ */
+ static void init(HINSTANCE);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get instance
+ /*!
+ Returns the application instance handle passed to init().
+ */
+ static HINSTANCE getWindowInstance();
+
+ //@}
+
+ // IScreen overrides
+ virtual void* getEventTarget() const;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const;
+
+ // IPrimaryScreen overrides
+ virtual void reconfigure(UInt32 activeSides);
+ virtual void warpCursor(SInt32 x, SInt32 y);
+ virtual UInt32 registerHotKey(KeyID key,
+ KeyModifierMask mask);
+ virtual void unregisterHotKey(UInt32 id);
+ virtual void fakeInputBegin();
+ virtual void fakeInputEnd();
+ virtual SInt32 getJumpZoneSize() const;
+ virtual bool isAnyMouseButtonDown(UInt32& buttonID) const;
+ virtual void getCursorCenter(SInt32& x, SInt32& y) const;
+
+ // ISecondaryScreen overrides
+ virtual void fakeMouseButton(ButtonID id, bool press);
+ virtual void fakeMouseMove(SInt32 x, SInt32 y);
+ virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const;
+ virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
+
+ // IKeyState overrides
+ virtual void updateKeys();
+ virtual void fakeKeyDown(KeyID id, KeyModifierMask mask,
+ KeyButton button);
+ virtual bool fakeKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button);
+ virtual bool fakeKeyUp(KeyButton button);
+ virtual void fakeAllKeysUp();
+
+ // IPlatformScreen overrides
+ virtual void enable();
+ virtual void disable();
+ virtual void enter();
+ virtual bool leave();
+ virtual bool setClipboard(ClipboardID, const IClipboard*);
+ virtual void checkClipboards();
+ virtual void openScreensaver(bool notify);
+ virtual void closeScreensaver();
+ virtual void screensaver(bool activate);
+ virtual void resetOptions();
+ virtual void setOptions(const OptionsList& options);
+ virtual void setSequenceNumber(UInt32);
+ virtual bool isPrimary() const;
+ virtual void fakeDraggingFiles(DragFileList fileList);
+ virtual String& getDraggingFilename();
+ virtual const String&
+ getDropTarget() const;
+
+protected:
+ // IPlatformScreen overrides
+ virtual void handleSystemEvent(const Event&, void*);
+ virtual void updateButtons();
+ virtual IKeyState* getKeyState() const;
+
+ // simulate a local key to the system directly
+ void fakeLocalKey(KeyButton button, bool press) const;
+
+private:
+ // initialization and shutdown operations
+ HCURSOR createBlankCursor() const;
+ void destroyCursor(HCURSOR cursor) const;
+ ATOM createWindowClass() const;
+ ATOM createDeskWindowClass(bool isPrimary) const;
+ void destroyClass(ATOM windowClass) const;
+ HWND createWindow(ATOM windowClass, const char* name) const;
+ HWND createDropWindow(ATOM windowClass, const char* name) const;
+ void destroyWindow(HWND) const;
+
+ // convenience function to send events
+public: // HACK
+ void sendEvent(Event::Type type, void* = NULL);
+private: // HACK
+ void sendClipboardEvent(Event::Type type, ClipboardID id);
+
+ // handle message before it gets dispatched. returns true iff
+ // the message should not be dispatched.
+ bool onPreDispatch(HWND, UINT, WPARAM, LPARAM);
+
+ // handle message before it gets dispatched. returns true iff
+ // the message should not be dispatched.
+ bool onPreDispatchPrimary(HWND, UINT, WPARAM, LPARAM);
+
+ // handle message. returns true iff handled and optionally sets
+ // \c *result (which defaults to 0).
+ bool onEvent(HWND, UINT, WPARAM, LPARAM, LRESULT* result);
+
+ // message handlers
+ bool onMark(UInt32 mark);
+ bool onKey(WPARAM, LPARAM);
+ bool onHotKey(WPARAM, LPARAM);
+ bool onMouseButton(WPARAM, LPARAM);
+ bool onMouseMove(SInt32 x, SInt32 y);
+ bool onMouseWheel(SInt32 xDelta, SInt32 yDelta);
+ bool onScreensaver(bool activated);
+ bool onDisplayChange();
+ bool onClipboardChange();
+
+ // warp cursor without discarding queued events
+ void warpCursorNoFlush(SInt32 x, SInt32 y);
+
+ // discard posted messages
+ void nextMark();
+
+ // test if event should be ignored
+ bool ignore() const;
+
+ // update screen size cache
+ void updateScreenShape();
+
+ // fix timer callback
+ void handleFixes(const Event&, void*);
+
+ // fix the clipboard viewer chain
+ void fixClipboardViewer();
+
+ // enable/disable special key combinations so we can catch/pass them
+ void enableSpecialKeys(bool) const;
+
+ // map a button event to a button ID
+ ButtonID mapButtonFromEvent(WPARAM msg, LPARAM button) const;
+
+ // map a button event to a press (true) or release (false)
+ bool mapPressFromEvent(WPARAM msg, LPARAM button) const;
+
+ // job to update the key state
+ void updateKeysCB(void*);
+
+ // determine whether the mouse is hidden by the system and force
+ // it to be displayed if user has entered this secondary screen.
+ void forceShowCursor();
+
+ // forceShowCursor uses MouseKeys to show the cursor. since we
+ // don't actually want MouseKeys behavior we have to make sure
+ // it applies when NumLock is in whatever state it's not in now.
+ // this method does that.
+ void updateForceShowCursor();
+
+ // our window proc
+ static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
+
+ // save last position of mouse to compute next delta movement
+ void saveMousePosition(SInt32 x, SInt32 y);
+
+ // check if it is a modifier key repeating message
+ bool isModifierRepeat(KeyModifierMask oldState,
+ KeyModifierMask state, WPARAM wParam) const;
+
+ // send drag info and data back to server
+ void sendDragThread(void*);
+
+private:
+ struct HotKeyItem {
+ public:
+ HotKeyItem(UINT vk, UINT modifiers);
+
+ UINT getVirtualKey() const;
+
+ bool operator<(const HotKeyItem&) const;
+
+ private:
+ UINT m_keycode;
+ UINT m_mask;
+ };
+ typedef std::map<UInt32, HotKeyItem> HotKeyMap;
+ typedef std::vector<UInt32> HotKeyIDList;
+ typedef std::map<HotKeyItem, UInt32> HotKeyToIDMap;
+ typedef std::vector<KeyButton> PrimaryKeyDownList;
+
+ static HINSTANCE s_windowInstance;
+
+ // true if screen is being used as a primary screen, false otherwise
+ bool m_isPrimary;
+
+ // true if hooks are not to be installed (useful for debugging)
+ bool m_noHooks;
+
+ // true if mouse has entered the screen
+ bool m_isOnScreen;
+
+ // our resources
+ ATOM m_class;
+
+ // screen shape stuff
+ SInt32 m_x, m_y;
+ SInt32 m_w, m_h;
+ SInt32 m_xCenter, m_yCenter;
+
+ // true if system appears to have multiple monitors
+ bool m_multimon;
+
+ // last mouse position
+ SInt32 m_xCursor, m_yCursor;
+
+ // last clipboard
+ UInt32 m_sequenceNumber;
+
+ // used to discard queued messages that are no longer needed
+ UInt32 m_mark;
+ UInt32 m_markReceived;
+
+ // the main loop's thread id
+ DWORD m_threadID;
+
+ // timer for periodically checking stuff that requires polling
+ EventQueueTimer* m_fixTimer;
+
+ // the keyboard layout to use when off primary screen
+ HKL m_keyLayout;
+
+ // screen saver stuff
+ MSWindowsScreenSaver*
+ m_screensaver;
+ bool m_screensaverNotify;
+ bool m_screensaverActive;
+
+ // clipboard stuff. our window is used mainly as a clipboard
+ // owner and as a link in the clipboard viewer chain.
+ HWND m_window;
+ HWND m_nextClipboardWindow;
+ bool m_ownClipboard;
+
+ // one desk per desktop and a cond var to communicate with it
+ MSWindowsDesks* m_desks;
+
+ // keyboard stuff
+ MSWindowsKeyState* m_keyState;
+
+ // hot key stuff
+ HotKeyMap m_hotKeys;
+ HotKeyIDList m_oldHotKeyIDs;
+ HotKeyToIDMap m_hotKeyToIDMap;
+
+ // map of button state
+ bool m_buttons[1 + kButtonExtra0 + 1];
+
+ // the system shows the mouse cursor when an internal display count
+ // is >= 0. this count is maintained per application but there's
+ // apparently a system wide count added to the application's count.
+ // this system count is 0 if there's a mouse attached to the system
+ // and -1 otherwise. the MouseKeys accessibility feature can modify
+ // this system count by making the system appear to have a mouse.
+ //
+ // m_hasMouse is true iff there's a mouse attached to the system or
+ // MouseKeys is simulating one. we track this so we can force the
+ // cursor to be displayed when the user has entered this screen.
+ // m_showingMouse is true when we're doing that.
+ bool m_hasMouse;
+ bool m_showingMouse;
+ bool m_gotOldMouseKeys;
+ MOUSEKEYS m_mouseKeys;
+ MOUSEKEYS m_oldMouseKeys;
+
+ MSWindowsHook m_hook;
+
+ static MSWindowsScreen*
+ s_screen;
+
+ IEventQueue* m_events;
+
+ String m_desktopPath;
+
+ MSWindowsDropTarget*
+ m_dropTarget;
+ HWND m_dropWindow;
+ const int m_dropWindowSize;
+
+ Thread* m_sendDragThread;
+
+ PrimaryKeyDownList m_primaryKeyDownList;
+};
diff --git a/src/lib/platform/MSWindowsScreenSaver.cpp b/src/lib/platform/MSWindowsScreenSaver.cpp
new file mode 100644
index 0000000..f9c15fb
--- /dev/null
+++ b/src/lib/platform/MSWindowsScreenSaver.cpp
@@ -0,0 +1,359 @@
+/*
+ * barrier -- 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 "platform/MSWindowsScreenSaver.h"
+
+#include "platform/MSWindowsScreen.h"
+#include "mt/Thread.h"
+#include "arch/Arch.h"
+#include "arch/win32/ArchMiscWindows.h"
+#include "base/Log.h"
+#include "base/TMethodJob.h"
+
+#include <malloc.h>
+#include <tchar.h>
+
+#if !defined(SPI_GETSCREENSAVERRUNNING)
+#define SPI_GETSCREENSAVERRUNNING 114
+#endif
+
+static const TCHAR* g_isSecureNT = "ScreenSaverIsSecure";
+static const TCHAR* g_isSecure9x = "ScreenSaveUsePassword";
+static const TCHAR* const g_pathScreenSaverIsSecure[] = {
+ "Control Panel",
+ "Desktop",
+ NULL
+};
+
+//
+// MSWindowsScreenSaver
+//
+
+MSWindowsScreenSaver::MSWindowsScreenSaver() :
+ m_wasSecure(false),
+ m_wasSecureAnInt(false),
+ m_process(NULL),
+ m_watch(NULL),
+ m_threadID(0),
+ m_msg(0),
+ m_wParam(0),
+ m_lParam(0),
+ m_active(false)
+{
+ // check if screen saver is enabled
+ SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0);
+}
+
+MSWindowsScreenSaver::~MSWindowsScreenSaver()
+{
+ unwatchProcess();
+}
+
+bool
+MSWindowsScreenSaver::checkStarted(UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ // if already started then say it didn't just start
+ if (m_active) {
+ return false;
+ }
+
+ // screen saver may have started. look for it and get
+ // the process. if we can't find it then assume it
+ // didn't really start. we wait a moment before
+ // looking to give the screen saver a chance to start.
+ // this shouldn't be a problem since we only get here
+ // if the screen saver wants to kick in, meaning that
+ // the system is idle or the user deliberately started
+ // the screen saver.
+ Sleep(250);
+
+ // set parameters common to all screen saver handling
+ m_threadID = GetCurrentThreadId();
+ m_msg = msg;
+ m_wParam = wParam;
+ m_lParam = lParam;
+
+ // on the windows nt family we wait for the desktop to
+ // change until it's neither the Screen-Saver desktop
+ // nor a desktop we can't open (the login desktop).
+ // since windows will send the request-to-start-screen-
+ // saver message even when the screen saver is disabled
+ // we first check that the screen saver is indeed active
+ // before watching for it to stop.
+ if (!isActive()) {
+ LOG((CLOG_DEBUG2 "can't open screen saver desktop"));
+ return false;
+ }
+
+ watchDesktop();
+ return true;
+}
+
+void
+MSWindowsScreenSaver::enable()
+{
+ SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, m_wasEnabled, 0, 0);
+
+ // restore password protection
+ if (m_wasSecure) {
+ setSecure(true, m_wasSecureAnInt);
+ }
+
+ // restore display power down
+ ArchMiscWindows::removeBusyState(ArchMiscWindows::kDISPLAY);
+}
+
+void
+MSWindowsScreenSaver::disable()
+{
+ SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0);
+ SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, 0);
+
+ // disable password protected screensaver
+ m_wasSecure = isSecure(&m_wasSecureAnInt);
+ if (m_wasSecure) {
+ setSecure(false, m_wasSecureAnInt);
+ }
+
+ // disable display power down
+ ArchMiscWindows::addBusyState(ArchMiscWindows::kDISPLAY);
+}
+
+void
+MSWindowsScreenSaver::activate()
+{
+ // don't activate if already active
+ if (!isActive()) {
+ // activate
+ HWND hwnd = GetForegroundWindow();
+ if (hwnd != NULL) {
+ PostMessage(hwnd, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
+ }
+ else {
+ // no foreground window. pretend we got the event instead.
+ DefWindowProc(NULL, WM_SYSCOMMAND, SC_SCREENSAVE, 0);
+ }
+
+ // restore power save when screen saver activates
+ ArchMiscWindows::removeBusyState(ArchMiscWindows::kDISPLAY);
+ }
+}
+
+void
+MSWindowsScreenSaver::deactivate()
+{
+ bool killed = false;
+
+ // NT runs screen saver in another desktop
+ HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE,
+ DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
+ if (desktop != NULL) {
+ EnumDesktopWindows(desktop,
+ &MSWindowsScreenSaver::killScreenSaverFunc,
+ reinterpret_cast<LPARAM>(&killed));
+ CloseDesktop(desktop);
+ }
+
+ // if above failed or wasn't tried, try the windows 95 way
+ if (!killed) {
+ // find screen saver window and close it
+ HWND hwnd = FindWindow("WindowsScreenSaverClass", NULL);
+ if (hwnd == NULL) {
+ // win2k may use a different class
+ hwnd = FindWindow("Default Screen Saver", NULL);
+ }
+ if (hwnd != NULL) {
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+ }
+ }
+
+ // force timer to restart
+ SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, &m_wasEnabled, 0);
+ SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,
+ !m_wasEnabled, 0, SPIF_SENDWININICHANGE);
+ SystemParametersInfo(SPI_SETSCREENSAVEACTIVE,
+ m_wasEnabled, 0, SPIF_SENDWININICHANGE);
+
+ // disable display power down
+ ArchMiscWindows::removeBusyState(ArchMiscWindows::kDISPLAY);
+}
+
+bool
+MSWindowsScreenSaver::isActive() const
+{
+ BOOL running;
+ SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0);
+ return (running != FALSE);
+}
+
+BOOL CALLBACK
+MSWindowsScreenSaver::killScreenSaverFunc(HWND hwnd, LPARAM arg)
+{
+ if (IsWindowVisible(hwnd)) {
+ HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE);
+ if (instance != MSWindowsScreen::getWindowInstance()) {
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+ *reinterpret_cast<bool*>(arg) = true;
+ }
+ }
+ return TRUE;
+}
+
+void
+MSWindowsScreenSaver::watchDesktop()
+{
+ // stop watching previous process/desktop
+ unwatchProcess();
+
+ // watch desktop in another thread
+ LOG((CLOG_DEBUG "watching screen saver desktop"));
+ m_active = true;
+ m_watch = new Thread(new TMethodJob<MSWindowsScreenSaver>(this,
+ &MSWindowsScreenSaver::watchDesktopThread));
+}
+
+void
+MSWindowsScreenSaver::watchProcess(HANDLE process)
+{
+ // stop watching previous process/desktop
+ unwatchProcess();
+
+ // watch new process in another thread
+ if (process != NULL) {
+ LOG((CLOG_DEBUG "watching screen saver process"));
+ m_process = process;
+ m_active = true;
+ m_watch = new Thread(new TMethodJob<MSWindowsScreenSaver>(this,
+ &MSWindowsScreenSaver::watchProcessThread));
+ }
+}
+
+void
+MSWindowsScreenSaver::unwatchProcess()
+{
+ if (m_watch != NULL) {
+ LOG((CLOG_DEBUG "stopped watching screen saver process/desktop"));
+ m_watch->cancel();
+ m_watch->wait();
+ delete m_watch;
+ m_watch = NULL;
+ m_active = false;
+ }
+ if (m_process != NULL) {
+ CloseHandle(m_process);
+ m_process = NULL;
+ }
+}
+
+void
+MSWindowsScreenSaver::watchDesktopThread(void*)
+{
+ DWORD reserved = 0;
+ TCHAR* name = NULL;
+
+ for (;;) {
+ // wait a bit
+ ARCH->sleep(0.2);
+
+ BOOL running;
+ SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, 0);
+ if (running) {
+ continue;
+ }
+
+ // send screen saver deactivation message
+ m_active = false;
+ PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam);
+ return;
+ }
+}
+
+void
+MSWindowsScreenSaver::watchProcessThread(void*)
+{
+ for (;;) {
+ Thread::testCancel();
+ if (WaitForSingleObject(m_process, 50) == WAIT_OBJECT_0) {
+ // process terminated
+ LOG((CLOG_DEBUG "screen saver died"));
+
+ // send screen saver deactivation message
+ m_active = false;
+ PostThreadMessage(m_threadID, m_msg, m_wParam, m_lParam);
+ return;
+ }
+ }
+}
+
+void
+MSWindowsScreenSaver::setSecure(bool secure, bool saveSecureAsInt)
+{
+ HKEY hkey =
+ ArchMiscWindows::addKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure);
+ if (hkey == NULL) {
+ return;
+ }
+
+ if (saveSecureAsInt) {
+ ArchMiscWindows::setValue(hkey, g_isSecureNT, secure ? 1 : 0);
+ }
+ else {
+ ArchMiscWindows::setValue(hkey, g_isSecureNT, secure ? "1" : "0");
+ }
+
+ ArchMiscWindows::closeKey(hkey);
+}
+
+bool
+MSWindowsScreenSaver::isSecure(bool* wasSecureFlagAnInt) const
+{
+ // get the password protection setting key
+ HKEY hkey =
+ ArchMiscWindows::openKey(HKEY_CURRENT_USER, g_pathScreenSaverIsSecure);
+ if (hkey == NULL) {
+ return false;
+ }
+
+ // get the value. the value may be an int or a string, depending
+ // on the version of windows.
+ bool result;
+ switch (ArchMiscWindows::typeOfValue(hkey, g_isSecureNT)) {
+ default:
+ result = false;
+ break;
+
+ case ArchMiscWindows::kUINT: {
+ DWORD value =
+ ArchMiscWindows::readValueInt(hkey, g_isSecureNT);
+ *wasSecureFlagAnInt = true;
+ result = (value != 0);
+ break;
+ }
+
+ case ArchMiscWindows::kSTRING: {
+ std::string value =
+ ArchMiscWindows::readValueString(hkey, g_isSecureNT);
+ *wasSecureFlagAnInt = false;
+ result = (value != "0");
+ break;
+ }
+ }
+
+ ArchMiscWindows::closeKey(hkey);
+ return result;
+}
diff --git a/src/lib/platform/MSWindowsScreenSaver.h b/src/lib/platform/MSWindowsScreenSaver.h
new file mode 100644
index 0000000..91df181
--- /dev/null
+++ b/src/lib/platform/MSWindowsScreenSaver.h
@@ -0,0 +1,89 @@
+/*
+ * barrier -- 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 "barrier/IScreenSaver.h"
+#include "base/String.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+class Thread;
+
+//! Microsoft windows screen saver implementation
+class MSWindowsScreenSaver : public IScreenSaver {
+public:
+ MSWindowsScreenSaver();
+ virtual ~MSWindowsScreenSaver();
+
+ //! @name manipulators
+ //@{
+
+ //! Check if screen saver started
+ /*!
+ Check if the screen saver really started. Returns false if it
+ hasn't, true otherwise. When the screen saver stops, \c msg will
+ be posted to the current thread's message queue with the given
+ parameters.
+ */
+ bool checkStarted(UINT msg, WPARAM, LPARAM);
+
+ //@}
+
+ // IScreenSaver overrides
+ virtual void enable();
+ virtual void disable();
+ virtual void activate();
+ virtual void deactivate();
+ virtual bool isActive() const;
+
+private:
+ class FindScreenSaverInfo {
+ public:
+ HDESK m_desktop;
+ HWND m_window;
+ };
+
+ static BOOL CALLBACK killScreenSaverFunc(HWND hwnd, LPARAM lParam);
+
+ void watchDesktop();
+ void watchProcess(HANDLE process);
+ void unwatchProcess();
+ void watchDesktopThread(void*);
+ void watchProcessThread(void*);
+
+ void setSecure(bool secure, bool saveSecureAsInt);
+ bool isSecure(bool* wasSecureAnInt) const;
+
+private:
+ BOOL m_wasEnabled;
+ bool m_wasSecure;
+ bool m_wasSecureAnInt;
+
+ HANDLE m_process;
+ Thread* m_watch;
+ DWORD m_threadID;
+ UINT m_msg;
+ WPARAM m_wParam;
+ LPARAM m_lParam;
+
+ // checkActive state. true if the screen saver is being watched
+ // for deactivation (and is therefore active).
+ bool m_active;
+};
diff --git a/src/lib/platform/MSWindowsSession.cpp b/src/lib/platform/MSWindowsSession.cpp
new file mode 100644
index 0000000..63e8d8f
--- /dev/null
+++ b/src/lib/platform/MSWindowsSession.cpp
@@ -0,0 +1,195 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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 "platform/MSWindowsSession.h"
+
+#include "arch/win32/XArchWindows.h"
+#include "barrier/XBarrier.h"
+#include "base/Log.h"
+
+#include <Wtsapi32.h>
+
+MSWindowsSession::MSWindowsSession() :
+ m_activeSessionId(-1)
+{
+}
+
+MSWindowsSession::~MSWindowsSession()
+{
+}
+
+bool
+MSWindowsSession::isProcessInSession(const char* name, PHANDLE process = NULL)
+{
+ // first we need to take a snapshot of the running processes
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) {
+ LOG((CLOG_ERR "could not get process snapshot"));
+ throw XArch(new XArchEvalWindows());
+ }
+
+ PROCESSENTRY32 entry;
+ entry.dwSize = sizeof(PROCESSENTRY32);
+
+ // get the first process, and if we can't do that then it's
+ // unlikely we can go any further
+ BOOL gotEntry = Process32First(snapshot, &entry);
+ if (!gotEntry) {
+ LOG((CLOG_ERR "could not get first process entry"));
+ throw XArch(new XArchEvalWindows());
+ }
+
+ // used to record process names for debug info
+ std::list<std::string> nameList;
+
+ // now just iterate until we can find winlogon.exe pid
+ DWORD pid = 0;
+ while(gotEntry) {
+
+ // make sure we're not checking the system process
+ if (entry.th32ProcessID != 0) {
+
+ DWORD processSessionId;
+ BOOL pidToSidRet = ProcessIdToSessionId(
+ entry.th32ProcessID, &processSessionId);
+
+ if (!pidToSidRet) {
+ // if we can not acquire session associated with a specified process,
+ // simply ignore it
+ LOG((CLOG_ERR "could not get session id for process id %i", entry.th32ProcessID));
+ gotEntry = nextProcessEntry(snapshot, &entry);
+ continue;
+ }
+ else {
+ // only pay attention to processes in the active session
+ if (processSessionId == m_activeSessionId) {
+
+ // store the names so we can record them for debug
+ nameList.push_back(entry.szExeFile);
+
+ if (_stricmp(entry.szExeFile, name) == 0) {
+ pid = entry.th32ProcessID;
+ }
+ }
+ }
+
+ }
+
+ // now move on to the next entry (if we're not at the end)
+ gotEntry = nextProcessEntry(snapshot, &entry);
+ }
+
+ std::string nameListJoin;
+ for(std::list<std::string>::iterator it = nameList.begin();
+ it != nameList.end(); it++) {
+ nameListJoin.append(*it);
+ nameListJoin.append(", ");
+ }
+
+ LOG((CLOG_DEBUG "processes in session %d: %s",
+ m_activeSessionId, nameListJoin.c_str()));
+
+ CloseHandle(snapshot);
+
+ if (pid) {
+ if (process != NULL) {
+ // now get the process, which we'll use to get the process token.
+ LOG((CLOG_DEBUG "found %s in session %i", name, m_activeSessionId));
+ *process = OpenProcess(MAXIMUM_ALLOWED, FALSE, pid);
+ }
+ return true;
+ }
+ else {
+ LOG((CLOG_DEBUG "did not find %s in session %i", name, m_activeSessionId));
+ return false;
+ }
+}
+
+HANDLE
+MSWindowsSession::getUserToken(LPSECURITY_ATTRIBUTES security)
+{
+ HANDLE sourceToken;
+ if (!WTSQueryUserToken(m_activeSessionId, &sourceToken)) {
+ LOG((CLOG_ERR "could not get token from session %d", m_activeSessionId));
+ throw XArch(new XArchEvalWindows);
+ }
+
+ HANDLE newToken;
+ if (!DuplicateTokenEx(
+ sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security,
+ SecurityImpersonation, TokenPrimary, &newToken)) {
+
+ LOG((CLOG_ERR "could not duplicate token"));
+ throw XArch(new XArchEvalWindows);
+ }
+
+ LOG((CLOG_DEBUG "duplicated, new token: %i", newToken));
+ return newToken;
+}
+
+BOOL
+MSWindowsSession::hasChanged()
+{
+ return (m_activeSessionId != WTSGetActiveConsoleSessionId());
+}
+
+void
+MSWindowsSession::updateActiveSession()
+{
+ m_activeSessionId = WTSGetActiveConsoleSessionId();
+}
+
+
+BOOL
+MSWindowsSession::nextProcessEntry(HANDLE snapshot, LPPROCESSENTRY32 entry)
+{
+ BOOL gotEntry = Process32Next(snapshot, entry);
+ if (!gotEntry) {
+
+ DWORD err = GetLastError();
+ if (err != ERROR_NO_MORE_FILES) {
+
+ // only worry about error if it's not the end of the snapshot
+ LOG((CLOG_ERR "could not get next process entry"));
+ throw XArch(new XArchEvalWindows());
+ }
+ }
+
+ return gotEntry;
+}
+
+String
+MSWindowsSession::getActiveDesktopName()
+{
+ String result;
+ try {
+ HDESK hd = OpenInputDesktop(0, TRUE, GENERIC_READ);
+ if (hd != NULL) {
+ DWORD size;
+ GetUserObjectInformation(hd, UOI_NAME, NULL, 0, &size);
+ TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR));
+ GetUserObjectInformation(hd, UOI_NAME, name, size, &size);
+ result = name;
+ CloseDesktop(hd);
+ }
+ }
+ catch (std::exception& error) {
+ LOG((CLOG_ERR "failed to get active desktop name: %s", error.what()));
+ }
+
+ return result;
+}
diff --git a/src/lib/platform/MSWindowsSession.h b/src/lib/platform/MSWindowsSession.h
new file mode 100644
index 0000000..d777141
--- /dev/null
+++ b/src/lib/platform/MSWindowsSession.h
@@ -0,0 +1,52 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#include "base/String.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <Tlhelp32.h>
+
+class MSWindowsSession {
+public:
+ MSWindowsSession();
+ ~MSWindowsSession();
+
+ //!
+ /*!
+ Returns true if the session ID has changed since updateActiveSession was called.
+ */
+ BOOL hasChanged();
+
+ bool isProcessInSession(const char* name, PHANDLE process);
+
+ HANDLE getUserToken(LPSECURITY_ATTRIBUTES security);
+
+ DWORD getActiveSessionId() { return m_activeSessionId; }
+
+ void updateActiveSession();
+
+ String getActiveDesktopName();
+
+private:
+ BOOL nextProcessEntry(HANDLE snapshot, LPPROCESSENTRY32 entry);
+
+private:
+ DWORD m_activeSessionId;
+};
diff --git a/src/lib/platform/MSWindowsUtil.cpp b/src/lib/platform/MSWindowsUtil.cpp
new file mode 100644
index 0000000..4b51781
--- /dev/null
+++ b/src/lib/platform/MSWindowsUtil.cpp
@@ -0,0 +1,64 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/MSWindowsUtil.h"
+
+#include "base/String.h"
+
+#include <stdio.h>
+
+//
+// MSWindowsUtil
+//
+
+String
+MSWindowsUtil::getString(HINSTANCE instance, DWORD id)
+{
+ char* msg = NULL;
+ int n = LoadString(instance, id, reinterpret_cast<LPSTR>(&msg), 0);
+
+ if (n <= 0) {
+ return String();
+ }
+
+ return String (msg, n);
+}
+
+String
+MSWindowsUtil::getErrorString(HINSTANCE hinstance, DWORD error, DWORD id)
+{
+ char* buffer;
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ 0,
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&buffer,
+ 0,
+ NULL) == 0) {
+ String errorString = barrier::string::sprintf("%d", error);
+ return barrier::string::format(getString(hinstance, id).c_str(),
+ errorString.c_str());
+ }
+ else {
+ String result(buffer);
+ LocalFree(buffer);
+ return result;
+ }
+}
diff --git a/src/lib/platform/MSWindowsUtil.h b/src/lib/platform/MSWindowsUtil.h
new file mode 100644
index 0000000..95ec2cf
--- /dev/null
+++ b/src/lib/platform/MSWindowsUtil.h
@@ -0,0 +1,40 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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"
+
+#define WINDOWS_LEAN_AND_MEAN
+#include <Windows.h>
+
+class MSWindowsUtil {
+public:
+ //! Get message string
+ /*!
+ Gets a string for \p id from the string table of \p instance.
+ */
+ static String getString(HINSTANCE instance, DWORD id);
+
+ //! Get error string
+ /*!
+ Gets a system error message for \p error. If the error cannot be
+ found return the string for \p id, replacing ${1} with \p error.
+ */
+ static String getErrorString(HINSTANCE, DWORD error, DWORD id);
+};
diff --git a/src/lib/platform/MSWindowsWatchdog.cpp b/src/lib/platform/MSWindowsWatchdog.cpp
new file mode 100644
index 0000000..ba1890e
--- /dev/null
+++ b/src/lib/platform/MSWindowsWatchdog.cpp
@@ -0,0 +1,611 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2009 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 "platform/MSWindowsWatchdog.h"
+
+#include "ipc/IpcLogOutputter.h"
+#include "ipc/IpcServer.h"
+#include "ipc/IpcMessage.h"
+#include "ipc/Ipc.h"
+#include "barrier/App.h"
+#include "barrier/ArgsBase.h"
+#include "mt/Thread.h"
+#include "arch/win32/ArchDaemonWindows.h"
+#include "arch/win32/XArchWindows.h"
+#include "arch/Arch.h"
+#include "base/log_outputters.h"
+#include "base/TMethodJob.h"
+#include "base/Log.h"
+#include "common/Version.h"
+
+#include <sstream>
+#include <UserEnv.h>
+#include <Shellapi.h>
+
+#define MAXIMUM_WAIT_TIME 3
+enum {
+ kOutputBufferSize = 4096
+};
+
+typedef VOID (WINAPI *SendSas)(BOOL asUser);
+
+const char g_activeDesktop[] = {"activeDesktop:"};
+
+MSWindowsWatchdog::MSWindowsWatchdog(
+ bool daemonized,
+ bool autoDetectCommand,
+ IpcServer& ipcServer,
+ IpcLogOutputter& ipcLogOutputter) :
+ m_thread(NULL),
+ m_autoDetectCommand(autoDetectCommand),
+ m_monitoring(true),
+ m_commandChanged(false),
+ m_stdOutWrite(NULL),
+ m_stdOutRead(NULL),
+ m_ipcServer(ipcServer),
+ m_ipcLogOutputter(ipcLogOutputter),
+ m_elevateProcess(false),
+ m_processFailures(0),
+ m_processRunning(false),
+ m_fileLogOutputter(NULL),
+ m_autoElevated(false),
+ m_ready(false),
+ m_daemonized(daemonized)
+{
+ m_mutex = ARCH->newMutex();
+ m_condVar = ARCH->newCondVar();
+}
+
+MSWindowsWatchdog::~MSWindowsWatchdog()
+{
+ if (m_condVar != NULL) {
+ ARCH->closeCondVar(m_condVar);
+ }
+
+ if (m_mutex != NULL) {
+ ARCH->closeMutex(m_mutex);
+ }
+}
+
+void
+MSWindowsWatchdog::startAsync()
+{
+ m_thread = new Thread(new TMethodJob<MSWindowsWatchdog>(
+ this, &MSWindowsWatchdog::mainLoop, nullptr));
+
+ m_outputThread = new Thread(new TMethodJob<MSWindowsWatchdog>(
+ this, &MSWindowsWatchdog::outputLoop, nullptr));
+}
+
+void
+MSWindowsWatchdog::stop()
+{
+ m_monitoring = false;
+
+ m_thread->wait(5);
+ delete m_thread;
+
+ m_outputThread->wait(5);
+ delete m_outputThread;
+}
+
+HANDLE
+MSWindowsWatchdog::duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security)
+{
+ HANDLE sourceToken;
+
+ BOOL tokenRet = OpenProcessToken(
+ process,
+ TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS,
+ &sourceToken);
+
+ if (!tokenRet) {
+ LOG((CLOG_ERR "could not open token, process handle: %d", process));
+ throw XArch(new XArchEvalWindows());
+ }
+
+ LOG((CLOG_DEBUG "got token %i, duplicating", sourceToken));
+
+ HANDLE newToken;
+ BOOL duplicateRet = DuplicateTokenEx(
+ sourceToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, security,
+ SecurityImpersonation, TokenPrimary, &newToken);
+
+ if (!duplicateRet) {
+ LOG((CLOG_ERR "could not duplicate token %i", sourceToken));
+ throw XArch(new XArchEvalWindows());
+ }
+
+ LOG((CLOG_DEBUG "duplicated, new token: %i", newToken));
+ return newToken;
+}
+
+HANDLE
+MSWindowsWatchdog::getUserToken(LPSECURITY_ATTRIBUTES security)
+{
+ // always elevate if we are at the vista/7 login screen. we could also
+ // elevate for the uac dialog (consent.exe) but this would be pointless,
+ // since barrier would re-launch as non-elevated after the desk switch,
+ // and so would be unusable with the new elevated process taking focus.
+ if (m_elevateProcess || m_autoElevated) {
+ LOG((CLOG_DEBUG "getting elevated token, %s",
+ (m_elevateProcess ? "elevation required" : "at login screen")));
+
+ HANDLE process;
+ if (!m_session.isProcessInSession("winlogon.exe", &process)) {
+ throw XMSWindowsWatchdogError("cannot get user token without winlogon.exe");
+ }
+
+ return duplicateProcessToken(process, security);
+ } else {
+ LOG((CLOG_DEBUG "getting non-elevated token"));
+ return m_session.getUserToken(security);
+ }
+}
+
+void
+MSWindowsWatchdog::mainLoop(void*)
+{
+ shutdownExistingProcesses();
+
+ SendSas sendSasFunc = NULL;
+ HINSTANCE sasLib = LoadLibrary("sas.dll");
+ if (sasLib) {
+ LOG((CLOG_DEBUG "found sas.dll"));
+ sendSasFunc = (SendSas)GetProcAddress(sasLib, "SendSAS");
+ }
+
+ SECURITY_ATTRIBUTES saAttr;
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&m_stdOutRead, &m_stdOutWrite, &saAttr, 0)) {
+ throw XArch(new XArchEvalWindows());
+ }
+
+ ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
+
+ while (m_monitoring) {
+ try {
+
+ if (m_processRunning && getCommand().empty()) {
+ LOG((CLOG_INFO "process started but command is empty, shutting down"));
+ shutdownExistingProcesses();
+ m_processRunning = false;
+ continue;
+ }
+
+ if (m_processFailures != 0) {
+ // increasing backoff period, maximum of 10 seconds.
+ int timeout = (m_processFailures * 2) < 10 ? (m_processFailures * 2) : 10;
+ LOG((CLOG_INFO "backing off, wait=%ds, failures=%d", timeout, m_processFailures));
+ ARCH->sleep(timeout);
+ }
+
+ if (!getCommand().empty() && ((m_processFailures != 0) || m_session.hasChanged() || m_commandChanged)) {
+ startProcess();
+ }
+
+ if (m_processRunning && !isProcessActive()) {
+
+ m_processFailures++;
+ m_processRunning = false;
+
+ LOG((CLOG_WARN "detected application not running, pid=%d",
+ m_processInfo.dwProcessId));
+ }
+
+ if (sendSasFunc != NULL) {
+
+ HANDLE sendSasEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\SendSAS");
+ if (sendSasEvent != NULL) {
+
+ // use SendSAS event to wait for next session (timeout 1 second).
+ if (WaitForSingleObject(sendSasEvent, 1000) == WAIT_OBJECT_0) {
+ LOG((CLOG_DEBUG "calling SendSAS"));
+ sendSasFunc(FALSE);
+ }
+
+ CloseHandle(sendSasEvent);
+ continue;
+ }
+ }
+
+ // if the sas event failed, wait by sleeping.
+ ARCH->sleep(1);
+
+ }
+ catch (std::exception& e) {
+ LOG((CLOG_ERR "failed to launch, error: %s", e.what()));
+ m_processFailures++;
+ m_processRunning = false;
+ continue;
+ }
+ catch (...) {
+ LOG((CLOG_ERR "failed to launch, unknown error."));
+ m_processFailures++;
+ m_processRunning = false;
+ continue;
+ }
+ }
+
+ if (m_processRunning) {
+ LOG((CLOG_DEBUG "terminated running process on exit"));
+ shutdownProcess(m_processInfo.hProcess, m_processInfo.dwProcessId, 20);
+ }
+
+ LOG((CLOG_DEBUG "watchdog main thread finished"));
+}
+
+bool
+MSWindowsWatchdog::isProcessActive()
+{
+ DWORD exitCode;
+ GetExitCodeProcess(m_processInfo.hProcess, &exitCode);
+ return exitCode == STILL_ACTIVE;
+}
+
+void
+MSWindowsWatchdog::setFileLogOutputter(FileLogOutputter* outputter)
+{
+ m_fileLogOutputter = outputter;
+}
+
+void
+MSWindowsWatchdog::startProcess()
+{
+ if (m_command.empty()) {
+ throw XMSWindowsWatchdogError("cannot start process, command is empty");
+ }
+
+ m_commandChanged = false;
+
+ if (m_processRunning) {
+ LOG((CLOG_DEBUG "closing existing process to make way for new one"));
+ shutdownProcess(m_processInfo.hProcess, m_processInfo.dwProcessId, 20);
+ m_processRunning = false;
+ }
+
+ m_session.updateActiveSession();
+
+ BOOL createRet;
+ if (!m_daemonized) {
+ createRet = doStartProcessAsSelf(m_command);
+ } else {
+ SECURITY_ATTRIBUTES sa;
+ ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
+
+ getActiveDesktop(&sa);
+
+ ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
+ HANDLE userToken = getUserToken(&sa);
+ m_elevateProcess = m_autoElevated ? m_autoElevated : m_elevateProcess;
+ m_autoElevated = false;
+
+ // patch by Jack Zhou and Henry Tung
+ // set UIAccess to fix Windows 8 GUI interaction
+ // http://symless.com/spit/issues/details/3338/#c70
+ DWORD uiAccess = 1;
+ SetTokenInformation(userToken, TokenUIAccess, &uiAccess, sizeof(DWORD));
+
+ createRet = doStartProcessAsUser(m_command, userToken, &sa);
+ }
+
+ if (!createRet) {
+ LOG((CLOG_ERR "could not launch"));
+ DWORD exitCode = 0;
+ GetExitCodeProcess(m_processInfo.hProcess, &exitCode);
+ LOG((CLOG_ERR "exit code: %d", exitCode));
+ throw XArch(new XArchEvalWindows);
+ }
+ else {
+ // wait for program to fail.
+ ARCH->sleep(1);
+ if (!isProcessActive()) {
+ throw XMSWindowsWatchdogError("process immediately stopped");
+ }
+
+ m_processRunning = true;
+ m_processFailures = 0;
+
+ LOG((CLOG_DEBUG "started process, session=%i, elevated: %s, command=%s",
+ m_session.getActiveSessionId(),
+ m_elevateProcess ? "yes" : "no",
+ m_command.c_str()));
+ }
+}
+
+BOOL
+MSWindowsWatchdog::doStartProcessAsSelf(String& command)
+{
+ DWORD creationFlags =
+ NORMAL_PRIORITY_CLASS |
+ CREATE_NO_WINDOW |
+ CREATE_UNICODE_ENVIRONMENT;
+
+ STARTUPINFO si;
+ ZeroMemory(&si, sizeof(STARTUPINFO));
+ si.cb = sizeof(STARTUPINFO);
+ si.lpDesktop = "winsta0\\Default"; // TODO: maybe this should be \winlogon if we have logonui.exe?
+ si.hStdError = m_stdOutWrite;
+ si.hStdOutput = m_stdOutWrite;
+ si.dwFlags |= STARTF_USESTDHANDLES;
+
+ LOG((CLOG_INFO "starting new process as self"));
+ return CreateProcess(NULL, LPSTR(command.c_str()), NULL, NULL, FALSE, creationFlags, NULL, NULL, &si, &m_processInfo);
+}
+
+BOOL
+MSWindowsWatchdog::doStartProcessAsUser(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa)
+{
+ // clear, as we're reusing process info struct
+ ZeroMemory(&m_processInfo, sizeof(PROCESS_INFORMATION));
+
+ STARTUPINFO si;
+ ZeroMemory(&si, sizeof(STARTUPINFO));
+ si.cb = sizeof(STARTUPINFO);
+ si.lpDesktop = "winsta0\\Default"; // TODO: maybe this should be \winlogon if we have logonui.exe?
+ si.hStdError = m_stdOutWrite;
+ si.hStdOutput = m_stdOutWrite;
+ si.dwFlags |= STARTF_USESTDHANDLES;
+
+ LPVOID environment;
+ BOOL blockRet = CreateEnvironmentBlock(&environment, userToken, FALSE);
+ if (!blockRet) {
+ LOG((CLOG_ERR "could not create environment block"));
+ throw XArch(new XArchEvalWindows);
+ }
+
+ DWORD creationFlags =
+ NORMAL_PRIORITY_CLASS |
+ CREATE_NO_WINDOW |
+ CREATE_UNICODE_ENVIRONMENT;
+
+ // re-launch in current active user session
+ LOG((CLOG_INFO "starting new process as privileged user"));
+ BOOL createRet = CreateProcessAsUser(
+ userToken, NULL, LPSTR(command.c_str()),
+ sa, NULL, TRUE, creationFlags,
+ environment, NULL, &si, &m_processInfo);
+
+ DestroyEnvironmentBlock(environment);
+ CloseHandle(userToken);
+
+ return createRet;
+}
+
+void
+MSWindowsWatchdog::setCommand(const std::string& command, bool elevate)
+{
+ LOG((CLOG_INFO "service command updated"));
+ m_command = command;
+ m_elevateProcess = elevate;
+ m_commandChanged = true;
+ m_processFailures = 0;
+}
+
+std::string
+MSWindowsWatchdog::getCommand() const
+{
+ if (!m_autoDetectCommand) {
+ return m_command;
+ }
+
+ // seems like a fairly convoluted way to get the process name
+ const char* launchName = App::instance().argsBase().m_pname;
+ std::string args = ARCH->commandLine();
+
+ // build up a full command line
+ std::stringstream cmdTemp;
+ cmdTemp << launchName << args;
+
+ std::string cmd = cmdTemp.str();
+
+ size_t i;
+ std::string find = "--relaunch";
+ while ((i = cmd.find(find)) != std::string::npos) {
+ cmd.replace(i, find.length(), "");
+ }
+
+ return cmd;
+}
+
+void
+MSWindowsWatchdog::outputLoop(void*)
+{
+ // +1 char for \0
+ CHAR buffer[kOutputBufferSize + 1];
+
+ while (m_monitoring) {
+
+ DWORD bytesRead;
+ BOOL success = ReadFile(m_stdOutRead, buffer, kOutputBufferSize, &bytesRead, NULL);
+
+ // assume the process has gone away? slow down
+ // the reads until another one turns up.
+ if (!success || bytesRead == 0) {
+ ARCH->sleep(1);
+ }
+ else {
+ buffer[bytesRead] = '\0';
+
+ testOutput(buffer);
+
+ m_ipcLogOutputter.write(kINFO, buffer);
+
+ if (m_fileLogOutputter != NULL) {
+ m_fileLogOutputter->write(kINFO, buffer);
+ }
+ }
+ }
+}
+
+void
+MSWindowsWatchdog::shutdownProcess(HANDLE handle, DWORD pid, int timeout)
+{
+ DWORD exitCode;
+ GetExitCodeProcess(handle, &exitCode);
+ if (exitCode != STILL_ACTIVE) {
+ return;
+ }
+
+ IpcShutdownMessage shutdown;
+ m_ipcServer.send(shutdown, kIpcClientNode);
+
+ // wait for process to exit gracefully.
+ double start = ARCH->time();
+ while (true) {
+
+ GetExitCodeProcess(handle, &exitCode);
+ if (exitCode != STILL_ACTIVE) {
+ // yay, we got a graceful shutdown. there should be no hook in use errors!
+ LOG((CLOG_INFO "process %d was shutdown gracefully", pid));
+ break;
+ }
+ else {
+
+ double elapsed = (ARCH->time() - start);
+ if (elapsed > timeout) {
+ // if timeout reached, kill forcefully.
+ // calling TerminateProcess on barrier is very bad!
+ // it causes the hook DLL to stay loaded in some apps,
+ // making it impossible to start barrier again.
+ LOG((CLOG_WARN "shutdown timed out after %d secs, forcefully terminating", (int)elapsed));
+ TerminateProcess(handle, kExitSuccess);
+ break;
+ }
+
+ ARCH->sleep(1);
+ }
+ }
+}
+
+void
+MSWindowsWatchdog::shutdownExistingProcesses()
+{
+ // first we need to take a snapshot of the running processes
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) {
+ LOG((CLOG_ERR "could not get process snapshot"));
+ throw XArch(new XArchEvalWindows);
+ }
+
+ PROCESSENTRY32 entry;
+ entry.dwSize = sizeof(PROCESSENTRY32);
+
+ // get the first process, and if we can't do that then it's
+ // unlikely we can go any further
+ BOOL gotEntry = Process32First(snapshot, &entry);
+ if (!gotEntry) {
+ LOG((CLOG_ERR "could not get first process entry"));
+ throw XArch(new XArchEvalWindows);
+ }
+
+ // now just iterate until we can find winlogon.exe pid
+ DWORD pid = 0;
+ while (gotEntry) {
+
+ // make sure we're not checking the system process
+ if (entry.th32ProcessID != 0) {
+
+ if (_stricmp(entry.szExeFile, "barrierc.exe") == 0 ||
+ _stricmp(entry.szExeFile, "barriers.exe") == 0) {
+
+ HANDLE handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
+ shutdownProcess(handle, entry.th32ProcessID, 10);
+ }
+ }
+
+ // now move on to the next entry (if we're not at the end)
+ gotEntry = Process32Next(snapshot, &entry);
+ if (!gotEntry) {
+
+ DWORD err = GetLastError();
+ if (err != ERROR_NO_MORE_FILES) {
+
+ // only worry about error if it's not the end of the snapshot
+ LOG((CLOG_ERR "could not get subsiquent process entry"));
+ throw XArch(new XArchEvalWindows);
+ }
+ }
+ }
+
+ CloseHandle(snapshot);
+ m_processRunning = false;
+}
+
+void
+MSWindowsWatchdog::getActiveDesktop(LPSECURITY_ATTRIBUTES security)
+{
+ String installedDir = ARCH->getInstalledDirectory();
+ if (!installedDir.empty()) {
+ String syntoolCommand;
+ syntoolCommand.append("\"").append(installedDir).append("\\").append("syntool").append("\"");
+ syntoolCommand.append(" --get-active-desktop");
+
+ m_session.updateActiveSession();
+ bool elevateProcess = m_elevateProcess;
+ m_elevateProcess = true;
+ HANDLE userToken = getUserToken(security);
+ m_elevateProcess = elevateProcess;
+
+ BOOL createRet = doStartProcessAsUser(syntoolCommand, userToken, security);
+
+ if (!createRet) {
+ DWORD rc = GetLastError();
+ RevertToSelf();
+ }
+ else {
+ LOG((CLOG_DEBUG "launched syntool to check active desktop"));
+ }
+
+ ARCH->lockMutex(m_mutex);
+ int waitTime = 0;
+ while (!m_ready) {
+ if (waitTime >= MAXIMUM_WAIT_TIME) {
+ break;
+ }
+
+ ARCH->waitCondVar(m_condVar, m_mutex, 1.0);
+ waitTime++;
+ }
+ m_ready = false;
+ ARCH->unlockMutex(m_mutex);
+ }
+}
+
+void
+MSWindowsWatchdog::testOutput(String buffer)
+{
+ // HACK: check standard output seems hacky.
+ size_t i = buffer.find(g_activeDesktop);
+ if (i != String::npos) {
+ size_t s = sizeof(g_activeDesktop);
+ String defaultDesktop("Default");
+ String sub = buffer.substr(i + s - 1, defaultDesktop.size());
+ if (sub != defaultDesktop) {
+ m_autoElevated = true;
+ }
+
+ ARCH->lockMutex(m_mutex);
+ m_ready = true;
+ ARCH->broadcastCondVar(m_condVar);
+ ARCH->unlockMutex(m_mutex);
+ }
+}
diff --git a/src/lib/platform/MSWindowsWatchdog.h b/src/lib/platform/MSWindowsWatchdog.h
new file mode 100644
index 0000000..64ffab3
--- /dev/null
+++ b/src/lib/platform/MSWindowsWatchdog.h
@@ -0,0 +1,99 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2009 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 "platform/MSWindowsSession.h"
+#include "barrier/XBarrier.h"
+#include "arch/IArchMultithread.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <string>
+#include <list>
+
+class Thread;
+class IpcLogOutputter;
+class IpcServer;
+class FileLogOutputter;
+
+class MSWindowsWatchdog {
+public:
+ MSWindowsWatchdog(
+ bool daemonized,
+ bool autoDetectCommand,
+ IpcServer& ipcServer,
+ IpcLogOutputter& ipcLogOutputter);
+ virtual ~MSWindowsWatchdog();
+
+ void startAsync();
+ std::string getCommand() const;
+ void setCommand(const std::string& command, bool elevate);
+ void stop();
+ bool isProcessActive();
+ void setFileLogOutputter(FileLogOutputter* outputter);
+
+private:
+ void mainLoop(void*);
+ void outputLoop(void*);
+ void shutdownProcess(HANDLE handle, DWORD pid, int timeout);
+ void shutdownExistingProcesses();
+ HANDLE duplicateProcessToken(HANDLE process, LPSECURITY_ATTRIBUTES security);
+ HANDLE getUserToken(LPSECURITY_ATTRIBUTES security);
+ void startProcess();
+ BOOL doStartProcessAsUser(String& command, HANDLE userToken, LPSECURITY_ATTRIBUTES sa);
+ BOOL doStartProcessAsSelf(String& command);
+ void sendSas();
+ void getActiveDesktop(LPSECURITY_ATTRIBUTES security);
+ void testOutput(String buffer);
+
+private:
+ Thread* m_thread;
+ bool m_autoDetectCommand;
+ std::string m_command;
+ bool m_monitoring;
+ bool m_commandChanged;
+ HANDLE m_stdOutWrite;
+ HANDLE m_stdOutRead;
+ Thread* m_outputThread;
+ IpcServer& m_ipcServer;
+ IpcLogOutputter& m_ipcLogOutputter;
+ bool m_elevateProcess;
+ MSWindowsSession m_session;
+ PROCESS_INFORMATION m_processInfo;
+ int m_processFailures;
+ bool m_processRunning;
+ FileLogOutputter* m_fileLogOutputter;
+ bool m_autoElevated;
+ ArchMutex m_mutex;
+ ArchCond m_condVar;
+ bool m_ready;
+ bool m_daemonized;
+};
+
+//! Relauncher error
+/*!
+An error occured in the process watchdog.
+*/
+class XMSWindowsWatchdogError : public XBarrier {
+public:
+ XMSWindowsWatchdogError(const String& msg) : XBarrier(msg) { }
+
+ // XBase overrides
+ virtual String getWhat() const throw() { return what(); }
+};
diff --git a/src/lib/platform/OSXClipboard.cpp b/src/lib/platform/OSXClipboard.cpp
new file mode 100644
index 0000000..710b471
--- /dev/null
+++ b/src/lib/platform/OSXClipboard.cpp
@@ -0,0 +1,259 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXClipboard.h"
+
+#include "barrier/Clipboard.h"
+#include "platform/OSXClipboardUTF16Converter.h"
+#include "platform/OSXClipboardTextConverter.h"
+#include "platform/OSXClipboardBMPConverter.h"
+#include "platform/OSXClipboardHTMLConverter.h"
+#include "base/Log.h"
+#include "arch/XArch.h"
+
+//
+// OSXClipboard
+//
+
+OSXClipboard::OSXClipboard() :
+ m_time(0),
+ m_pboard(NULL)
+{
+ m_converters.push_back(new OSXClipboardHTMLConverter);
+ m_converters.push_back(new OSXClipboardBMPConverter);
+ m_converters.push_back(new OSXClipboardUTF16Converter);
+ m_converters.push_back(new OSXClipboardTextConverter);
+
+
+
+ OSStatus createErr = PasteboardCreate(kPasteboardClipboard, &m_pboard);
+ if (createErr != noErr) {
+ LOG((CLOG_DEBUG "failed to create clipboard reference: error %i", createErr));
+ LOG((CLOG_ERR "unable to connect to pasteboard, clipboard sharing disabled", createErr));
+ m_pboard = NULL;
+ return;
+
+ }
+
+ OSStatus syncErr = PasteboardSynchronize(m_pboard);
+ if (syncErr != noErr) {
+ LOG((CLOG_DEBUG "failed to syncronize clipboard: error %i", syncErr));
+ }
+}
+
+OSXClipboard::~OSXClipboard()
+{
+ clearConverters();
+}
+
+ bool
+OSXClipboard::empty()
+{
+ LOG((CLOG_DEBUG "emptying clipboard"));
+ if (m_pboard == NULL)
+ return false;
+
+ OSStatus err = PasteboardClear(m_pboard);
+ if (err != noErr) {
+ LOG((CLOG_DEBUG "failed to clear clipboard: error %i", err));
+ return false;
+ }
+
+ return true;
+}
+
+ bool
+OSXClipboard::synchronize()
+{
+ if (m_pboard == NULL)
+ return false;
+
+ PasteboardSyncFlags flags = PasteboardSynchronize(m_pboard);
+ LOG((CLOG_DEBUG2 "flags: %x", flags));
+
+ if (flags & kPasteboardModified) {
+ return true;
+ }
+ return false;
+}
+
+ void
+OSXClipboard::add(EFormat format, const String & data)
+{
+ if (m_pboard == NULL)
+ return;
+
+ LOG((CLOG_DEBUG "add %d bytes to clipboard format: %d", data.size(), format));
+ if (format == IClipboard::kText) {
+ LOG((CLOG_DEBUG " format of data to be added to clipboard was kText"));
+ }
+ else if (format == IClipboard::kBitmap) {
+ LOG((CLOG_DEBUG " format of data to be added to clipboard was kBitmap"));
+ }
+ else if (format == IClipboard::kHTML) {
+ LOG((CLOG_DEBUG " format of data to be added to clipboard was kHTML"));
+ }
+
+ for (ConverterList::const_iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+
+ IOSXClipboardConverter* converter = *index;
+
+ // skip converters for other formats
+ if (converter->getFormat() == format) {
+ String osXData = converter->fromIClipboard(data);
+ CFStringRef flavorType = converter->getOSXFormat();
+ CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (UInt8 *)osXData.data(), osXData.size());
+ PasteboardItemID itemID = 0;
+
+ PasteboardPutItemFlavor(
+ m_pboard,
+ itemID,
+ flavorType,
+ dataRef,
+ kPasteboardFlavorNoFlags);
+
+ LOG((CLOG_DEBUG "added %d bytes to clipboard format: %d", data.size(), format));
+ }
+
+ }
+}
+
+bool
+OSXClipboard::open(Time time) const
+{
+ if (m_pboard == NULL)
+ return false;
+
+ LOG((CLOG_DEBUG "opening clipboard"));
+ m_time = time;
+ return true;
+}
+
+void
+OSXClipboard::close() const
+{
+ LOG((CLOG_DEBUG "closing clipboard"));
+ /* not needed */
+}
+
+IClipboard::Time
+OSXClipboard::getTime() const
+{
+ return m_time;
+}
+
+bool
+OSXClipboard::has(EFormat format) const
+{
+ if (m_pboard == NULL)
+ return false;
+
+ PasteboardItemID item;
+ PasteboardGetItemIdentifier(m_pboard, (CFIndex) 1, &item);
+
+ for (ConverterList::const_iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ IOSXClipboardConverter* converter = *index;
+ if (converter->getFormat() == format) {
+ PasteboardFlavorFlags flags;
+ CFStringRef type = converter->getOSXFormat();
+
+ OSStatus res;
+
+ if ((res = PasteboardGetItemFlavorFlags(m_pboard, item, type, &flags)) == noErr) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+String
+OSXClipboard::get(EFormat format) const
+{
+ CFStringRef type;
+ PasteboardItemID item;
+ String result;
+
+ if (m_pboard == NULL)
+ return result;
+
+ PasteboardGetItemIdentifier(m_pboard, (CFIndex) 1, &item);
+
+
+ // find the converter for the first clipboard format we can handle
+ IOSXClipboardConverter* converter = NULL;
+ for (ConverterList::const_iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ converter = *index;
+
+ PasteboardFlavorFlags flags;
+ type = converter->getOSXFormat();
+
+ if (converter->getFormat() == format &&
+ PasteboardGetItemFlavorFlags(m_pboard, item, type, &flags) == noErr) {
+ break;
+ }
+ converter = NULL;
+ }
+
+ // if no converter then we don't recognize any formats
+ if (converter == NULL) {
+ LOG((CLOG_DEBUG "Unable to find converter for data"));
+ return result;
+ }
+
+ // get the clipboard data.
+ CFDataRef buffer = NULL;
+ try {
+ OSStatus err = PasteboardCopyItemFlavorData(m_pboard, item, type, &buffer);
+
+ if (err != noErr) {
+ throw err;
+ }
+
+ result = String((char *) CFDataGetBytePtr(buffer), CFDataGetLength(buffer));
+ }
+ catch (OSStatus err) {
+ LOG((CLOG_DEBUG "exception thrown in OSXClipboard::get MacError (%d)", err));
+ }
+ catch (...) {
+ LOG((CLOG_DEBUG "unknown exception in OSXClipboard::get"));
+ RETHROW_XTHREAD
+ }
+
+ if (buffer != NULL)
+ CFRelease(buffer);
+
+ return converter->toIClipboard(result);
+}
+
+ void
+OSXClipboard::clearConverters()
+{
+ if (m_pboard == NULL)
+ return;
+
+ for (ConverterList::iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ delete *index;
+ }
+ m_converters.clear();
+}
diff --git a/src/lib/platform/OSXClipboard.h b/src/lib/platform/OSXClipboard.h
new file mode 100644
index 0000000..ba25c32
--- /dev/null
+++ b/src/lib/platform/OSXClipboard.h
@@ -0,0 +1,95 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/IClipboard.h"
+
+#include <Carbon/Carbon.h>
+#include <vector>
+
+class IOSXClipboardConverter;
+
+//! OS X clipboard implementation
+class OSXClipboard : public IClipboard {
+public:
+ OSXClipboard();
+ virtual ~OSXClipboard();
+
+ //! Test if clipboard is owned by barrier
+ static bool isOwnedByBarrier();
+
+ // IClipboard overrides
+ virtual bool empty();
+ virtual void add(EFormat, const String& data);
+ virtual bool open(Time) const;
+ virtual void close() const;
+ virtual Time getTime() const;
+ virtual bool has(EFormat) const;
+ virtual String get(EFormat) const;
+
+ bool synchronize();
+private:
+ void clearConverters();
+
+private:
+ typedef std::vector<IOSXClipboardConverter*> ConverterList;
+
+ mutable Time m_time;
+ ConverterList m_converters;
+ PasteboardRef m_pboard;
+};
+
+//! Clipboard format converter interface
+/*!
+This interface defines the methods common to all Scrap book format
+*/
+class IOSXClipboardConverter : public IInterface {
+public:
+ //! @name accessors
+ //@{
+
+ //! Get clipboard format
+ /*!
+ Return the clipboard format this object converts from/to.
+ */
+ virtual IClipboard::EFormat
+ getFormat() const = 0;
+
+ //! returns the scrap flavor type that this object converts from/to
+ virtual CFStringRef
+ getOSXFormat() const = 0;
+
+ //! Convert from IClipboard format
+ /*!
+ Convert from the IClipboard format to the Carbon scrap format.
+ The input data must be in the IClipboard format returned by
+ getFormat(). The return data will be in the scrap
+ format returned by getOSXFormat().
+ */
+ virtual String fromIClipboard(const String&) const = 0;
+
+ //! Convert to IClipboard format
+ /*!
+ Convert from the carbon scrap format to the IClipboard format
+ (i.e., the reverse of fromIClipboard()).
+ */
+ virtual String toIClipboard(const String&) const = 0;
+
+ //@}
+};
diff --git a/src/lib/platform/OSXClipboardAnyBitmapConverter.cpp b/src/lib/platform/OSXClipboardAnyBitmapConverter.cpp
new file mode 100644
index 0000000..73f64c7
--- /dev/null
+++ b/src/lib/platform/OSXClipboardAnyBitmapConverter.cpp
@@ -0,0 +1,48 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 Symless Ltd.
+ * Patch by Ryan Chapman
+ *
+ * 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 "platform/OSXClipboardAnyBitmapConverter.h"
+#include <algorithm>
+
+OSXClipboardAnyBitmapConverter::OSXClipboardAnyBitmapConverter()
+{
+ // do nothing
+}
+
+OSXClipboardAnyBitmapConverter::~OSXClipboardAnyBitmapConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+OSXClipboardAnyBitmapConverter::getFormat() const
+{
+ return IClipboard::kBitmap;
+}
+
+String
+OSXClipboardAnyBitmapConverter::fromIClipboard(const String& data) const
+{
+ return doFromIClipboard(data);
+}
+
+String
+OSXClipboardAnyBitmapConverter::toIClipboard(const String& data) const
+{
+ return doToIClipboard(data);
+}
diff --git a/src/lib/platform/OSXClipboardAnyBitmapConverter.h b/src/lib/platform/OSXClipboardAnyBitmapConverter.h
new file mode 100644
index 0000000..277e75d
--- /dev/null
+++ b/src/lib/platform/OSXClipboardAnyBitmapConverter.h
@@ -0,0 +1,48 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 Symless Ltd.
+ * Patch by Ryan Chapman
+ *
+ * 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 "platform/OSXClipboard.h"
+
+//! Convert to/from some text encoding
+class OSXClipboardAnyBitmapConverter : public IOSXClipboardConverter {
+public:
+ OSXClipboardAnyBitmapConverter();
+ virtual ~OSXClipboardAnyBitmapConverter();
+
+ // IOSXClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual CFStringRef getOSXFormat() const = 0;
+ virtual String fromIClipboard(const String &) const;
+ virtual String toIClipboard(const String &) const;
+
+protected:
+ //! Convert from IClipboard format
+ /*!
+ Do UTF-8 conversion and linefeed conversion.
+ */
+ virtual String doFromIClipboard(const String&) const = 0;
+
+ //! Convert to IClipboard format
+ /*!
+ Do UTF-8 conversion and Linefeed conversion.
+ */
+ virtual String doToIClipboard(const String&) const = 0;
+};
diff --git a/src/lib/platform/OSXClipboardAnyTextConverter.cpp b/src/lib/platform/OSXClipboardAnyTextConverter.cpp
new file mode 100644
index 0000000..7095006
--- /dev/null
+++ b/src/lib/platform/OSXClipboardAnyTextConverter.cpp
@@ -0,0 +1,90 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXClipboardAnyTextConverter.h"
+
+#include <algorithm>
+
+//
+// OSXClipboardAnyTextConverter
+//
+
+OSXClipboardAnyTextConverter::OSXClipboardAnyTextConverter()
+{
+ // do nothing
+}
+
+OSXClipboardAnyTextConverter::~OSXClipboardAnyTextConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+OSXClipboardAnyTextConverter::getFormat() const
+{
+ return IClipboard::kText;
+}
+
+String
+OSXClipboardAnyTextConverter::fromIClipboard(const String& data) const
+{
+ // convert linefeeds and then convert to desired encoding
+ return doFromIClipboard(convertLinefeedToMacOS(data));
+}
+
+String
+OSXClipboardAnyTextConverter::toIClipboard(const String& data) const
+{
+ // convert text then newlines
+ return convertLinefeedToUnix(doToIClipboard(data));
+}
+
+static
+bool
+isLF(char ch)
+{
+ return (ch == '\n');
+}
+
+static
+bool
+isCR(char ch)
+{
+ return (ch == '\r');
+}
+
+String
+OSXClipboardAnyTextConverter::convertLinefeedToMacOS(const String& src)
+{
+ // note -- we assume src is a valid UTF-8 string
+ String copy = src;
+
+ std::replace_if(copy.begin(), copy.end(), isLF, '\r');
+
+ return copy;
+}
+
+String
+OSXClipboardAnyTextConverter::convertLinefeedToUnix(const String& src)
+{
+ String copy = src;
+
+ std::replace_if(copy.begin(), copy.end(), isCR, '\n');
+
+ return copy;
+}
diff --git a/src/lib/platform/OSXClipboardAnyTextConverter.h b/src/lib/platform/OSXClipboardAnyTextConverter.h
new file mode 100644
index 0000000..ea42994
--- /dev/null
+++ b/src/lib/platform/OSXClipboardAnyTextConverter.h
@@ -0,0 +1,53 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXClipboard.h"
+
+//! Convert to/from some text encoding
+class OSXClipboardAnyTextConverter : public IOSXClipboardConverter {
+public:
+ OSXClipboardAnyTextConverter();
+ virtual ~OSXClipboardAnyTextConverter();
+
+ // IOSXClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual CFStringRef
+ getOSXFormat() const = 0;
+ virtual String fromIClipboard(const String &) const;
+ virtual String toIClipboard(const String &) const;
+
+protected:
+ //! Convert from IClipboard format
+ /*!
+ Do UTF-8 conversion and linefeed conversion.
+ */
+ virtual String doFromIClipboard(const String&) const = 0;
+
+ //! Convert to IClipboard format
+ /*!
+ Do UTF-8 conversion and Linefeed conversion.
+ */
+ virtual String doToIClipboard(const String&) const = 0;
+
+private:
+ static String convertLinefeedToMacOS(const String&);
+ static String convertLinefeedToUnix(const String&);
+};
diff --git a/src/lib/platform/OSXClipboardBMPConverter.cpp b/src/lib/platform/OSXClipboardBMPConverter.cpp
new file mode 100644
index 0000000..51c44ec
--- /dev/null
+++ b/src/lib/platform/OSXClipboardBMPConverter.cpp
@@ -0,0 +1,134 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 Symless Ltd.
+ * Patch by Ryan Chapman
+ *
+ * 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 "platform/OSXClipboardBMPConverter.h"
+#include "base/Log.h"
+
+// BMP file header structure
+struct CBMPHeader {
+public:
+ UInt16 type;
+ UInt32 size;
+ UInt16 reserved1;
+ UInt16 reserved2;
+ UInt32 offset;
+};
+
+// BMP is little-endian
+static inline
+UInt32
+fromLEU32(const UInt8* data)
+{
+ return static_cast<UInt32>(data[0]) |
+ (static_cast<UInt32>(data[1]) << 8) |
+ (static_cast<UInt32>(data[2]) << 16) |
+ (static_cast<UInt32>(data[3]) << 24);
+}
+
+static
+void
+toLE(UInt8*& dst, char src)
+{
+ dst[0] = static_cast<UInt8>(src);
+ dst += 1;
+}
+
+static
+void
+toLE(UInt8*& dst, UInt16 src)
+{
+ dst[0] = static_cast<UInt8>(src & 0xffu);
+ dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
+ dst += 2;
+}
+
+static
+void
+toLE(UInt8*& dst, UInt32 src)
+{
+ dst[0] = static_cast<UInt8>(src & 0xffu);
+ dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
+ dst[2] = static_cast<UInt8>((src >> 16) & 0xffu);
+ dst[3] = static_cast<UInt8>((src >> 24) & 0xffu);
+ dst += 4;
+}
+
+OSXClipboardBMPConverter::OSXClipboardBMPConverter()
+{
+ // do nothing
+}
+
+OSXClipboardBMPConverter::~OSXClipboardBMPConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+OSXClipboardBMPConverter::getFormat() const
+{
+ return IClipboard::kBitmap;
+}
+
+CFStringRef
+OSXClipboardBMPConverter::getOSXFormat() const
+{
+ // TODO: does this only work with Windows?
+ return CFSTR("com.microsoft.bmp");
+}
+
+String
+OSXClipboardBMPConverter::fromIClipboard(const String& bmp) const
+{
+ LOG((CLOG_DEBUG1 "ENTER OSXClipboardBMPConverter::doFromIClipboard()"));
+ // create BMP image
+ UInt8 header[14];
+ UInt8* dst = header;
+ toLE(dst, 'B');
+ toLE(dst, 'M');
+ toLE(dst, static_cast<UInt32>(14 + bmp.size()));
+ toLE(dst, static_cast<UInt16>(0));
+ toLE(dst, static_cast<UInt16>(0));
+ toLE(dst, static_cast<UInt32>(14 + 40));
+ return String(reinterpret_cast<const char*>(header), 14) + bmp;
+}
+
+String
+OSXClipboardBMPConverter::toIClipboard(const String& bmp) const
+{
+ // make sure data is big enough for a BMP file
+ if (bmp.size() <= 14 + 40) {
+ return String();
+ }
+
+ // check BMP file header
+ const UInt8* rawBMPHeader = reinterpret_cast<const UInt8*>(bmp.data());
+ if (rawBMPHeader[0] != 'B' || rawBMPHeader[1] != 'M') {
+ return String();
+ }
+
+ // get offset to image data
+ UInt32 offset = fromLEU32(rawBMPHeader + 10);
+
+ // construct BMP
+ if (offset == 14 + 40) {
+ return bmp.substr(14);
+ }
+ else {
+ return bmp.substr(14, 40) + bmp.substr(offset, bmp.size() - offset);
+ }
+}
diff --git a/src/lib/platform/OSXClipboardBMPConverter.h b/src/lib/platform/OSXClipboardBMPConverter.h
new file mode 100644
index 0000000..400831d
--- /dev/null
+++ b/src/lib/platform/OSXClipboardBMPConverter.h
@@ -0,0 +1,44 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 Symless Ltd.
+ * Patch by Ryan Chapman
+ *
+ * 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 "platform/OSXClipboard.h"
+
+//! Convert to/from some text encoding
+class OSXClipboardBMPConverter : public IOSXClipboardConverter {
+public:
+ OSXClipboardBMPConverter();
+ virtual ~OSXClipboardBMPConverter();
+
+ // IMSWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+
+ virtual CFStringRef
+ getOSXFormat() const;
+
+ // OSXClipboardAnyBMPConverter overrides
+ virtual String fromIClipboard(const String&) const;
+ virtual String toIClipboard(const String&) const;
+
+ // generic encoding converter
+ static String convertString(const String& data,
+ CFStringEncoding fromEncoding,
+ CFStringEncoding toEncoding);
+};
diff --git a/src/lib/platform/OSXClipboardHTMLConverter.cpp b/src/lib/platform/OSXClipboardHTMLConverter.cpp
new file mode 100644
index 0000000..b5fdb77
--- /dev/null
+++ b/src/lib/platform/OSXClipboardHTMLConverter.cpp
@@ -0,0 +1,95 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 Symless Ltd.
+ * Patch by Ryan Chapman
+ *
+ * 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 "platform/OSXClipboardHTMLConverter.h"
+
+#include "base/Unicode.h"
+
+OSXClipboardHTMLConverter::OSXClipboardHTMLConverter()
+{
+ // do nothing
+}
+
+OSXClipboardHTMLConverter::~OSXClipboardHTMLConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+OSXClipboardHTMLConverter::getFormat() const
+{
+ return IClipboard::kHTML;
+}
+
+CFStringRef
+OSXClipboardHTMLConverter::getOSXFormat() const
+{
+ return CFSTR("public.html");
+}
+
+String
+OSXClipboardHTMLConverter::convertString(
+ const String& data,
+ CFStringEncoding fromEncoding,
+ CFStringEncoding toEncoding)
+{
+ CFStringRef stringRef = CFStringCreateWithCString(
+ kCFAllocatorDefault,
+ data.c_str(), fromEncoding);
+
+ if (stringRef == NULL) {
+ return String();
+ }
+
+ CFIndex buffSize;
+ CFRange entireString = CFRangeMake(0, CFStringGetLength(stringRef));
+
+ CFStringGetBytes(stringRef, entireString, toEncoding,
+ 0, false, NULL, 0, &buffSize);
+
+ char* buffer = new char[buffSize];
+
+ if (buffer == NULL) {
+ CFRelease(stringRef);
+ return String();
+ }
+
+ CFStringGetBytes(stringRef, entireString, toEncoding,
+ 0, false, (UInt8*)buffer, buffSize, NULL);
+
+ String result(buffer, buffSize);
+
+ delete[] buffer;
+ CFRelease(stringRef);
+
+ return result;
+}
+
+String
+OSXClipboardHTMLConverter::doFromIClipboard(const String& data) const
+{
+ return convertString(data, kCFStringEncodingUTF8,
+ CFStringGetSystemEncoding());
+}
+
+String
+OSXClipboardHTMLConverter::doToIClipboard(const String& data) const
+{
+ return convertString(data, CFStringGetSystemEncoding(),
+ kCFStringEncodingUTF8);
+}
diff --git a/src/lib/platform/OSXClipboardHTMLConverter.h b/src/lib/platform/OSXClipboardHTMLConverter.h
new file mode 100644
index 0000000..21c2b82
--- /dev/null
+++ b/src/lib/platform/OSXClipboardHTMLConverter.h
@@ -0,0 +1,44 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2014-2016 Symless Ltd.
+ * Patch by Ryan Chapman
+ *
+ * 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 "OSXClipboardAnyTextConverter.h"
+
+//! Convert to/from HTML encoding
+class OSXClipboardHTMLConverter : public OSXClipboardAnyTextConverter {
+public:
+ OSXClipboardHTMLConverter();
+ virtual ~OSXClipboardHTMLConverter();
+
+ // IMSWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+
+ virtual CFStringRef getOSXFormat() const;
+
+protected:
+ // OSXClipboardAnyTextConverter overrides
+ virtual String doFromIClipboard(const String&) const;
+ virtual String doToIClipboard(const String&) const;
+
+ // generic encoding converter
+ static String convertString(const String& data,
+ CFStringEncoding fromEncoding,
+ CFStringEncoding toEncoding);
+};
diff --git a/src/lib/platform/OSXClipboardTextConverter.cpp b/src/lib/platform/OSXClipboardTextConverter.cpp
new file mode 100644
index 0000000..c18ad3f
--- /dev/null
+++ b/src/lib/platform/OSXClipboardTextConverter.cpp
@@ -0,0 +1,93 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXClipboardTextConverter.h"
+
+#include "base/Unicode.h"
+
+//
+// OSXClipboardTextConverter
+//
+
+OSXClipboardTextConverter::OSXClipboardTextConverter()
+{
+ // do nothing
+}
+
+OSXClipboardTextConverter::~OSXClipboardTextConverter()
+{
+ // do nothing
+}
+
+CFStringRef
+OSXClipboardTextConverter::getOSXFormat() const
+{
+ return CFSTR("public.plain-text");
+}
+
+String
+OSXClipboardTextConverter::convertString(
+ const String& data,
+ CFStringEncoding fromEncoding,
+ CFStringEncoding toEncoding)
+{
+ CFStringRef stringRef =
+ CFStringCreateWithCString(kCFAllocatorDefault,
+ data.c_str(), fromEncoding);
+
+ if (stringRef == NULL) {
+ return String();
+ }
+
+ CFIndex buffSize;
+ CFRange entireString = CFRangeMake(0, CFStringGetLength(stringRef));
+
+ CFStringGetBytes(stringRef, entireString, toEncoding,
+ 0, false, NULL, 0, &buffSize);
+
+ char* buffer = new char[buffSize];
+
+ if (buffer == NULL) {
+ CFRelease(stringRef);
+ return String();
+ }
+
+ CFStringGetBytes(stringRef, entireString, toEncoding,
+ 0, false, (UInt8*)buffer, buffSize, NULL);
+
+ String result(buffer, buffSize);
+
+ delete[] buffer;
+ CFRelease(stringRef);
+
+ return result;
+}
+
+String
+OSXClipboardTextConverter::doFromIClipboard(const String& data) const
+{
+ return convertString(data, kCFStringEncodingUTF8,
+ CFStringGetSystemEncoding());
+}
+
+String
+OSXClipboardTextConverter::doToIClipboard(const String& data) const
+{
+ return convertString(data, CFStringGetSystemEncoding(),
+ kCFStringEncodingUTF8);
+}
diff --git a/src/lib/platform/OSXClipboardTextConverter.h b/src/lib/platform/OSXClipboardTextConverter.h
new file mode 100644
index 0000000..55d82ce
--- /dev/null
+++ b/src/lib/platform/OSXClipboardTextConverter.h
@@ -0,0 +1,42 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXClipboardAnyTextConverter.h"
+
+//! Convert to/from locale text encoding
+class OSXClipboardTextConverter : public OSXClipboardAnyTextConverter {
+public:
+ OSXClipboardTextConverter();
+ virtual ~OSXClipboardTextConverter();
+
+ // IOSXClipboardAnyTextConverter overrides
+ virtual CFStringRef
+ getOSXFormat() const;
+
+protected:
+ // OSXClipboardAnyTextConverter overrides
+ virtual String doFromIClipboard(const String&) const;
+ virtual String doToIClipboard(const String&) const;
+
+ // generic encoding converter
+ static String convertString(const String& data,
+ CFStringEncoding fromEncoding,
+ CFStringEncoding toEncoding);
+};
diff --git a/src/lib/platform/OSXClipboardUTF16Converter.cpp b/src/lib/platform/OSXClipboardUTF16Converter.cpp
new file mode 100644
index 0000000..02d8fa3
--- /dev/null
+++ b/src/lib/platform/OSXClipboardUTF16Converter.cpp
@@ -0,0 +1,55 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXClipboardUTF16Converter.h"
+
+#include "base/Unicode.h"
+
+//
+// OSXClipboardUTF16Converter
+//
+
+OSXClipboardUTF16Converter::OSXClipboardUTF16Converter()
+{
+ // do nothing
+}
+
+OSXClipboardUTF16Converter::~OSXClipboardUTF16Converter()
+{
+ // do nothing
+}
+
+CFStringRef
+OSXClipboardUTF16Converter::getOSXFormat() const
+{
+ return CFSTR("public.utf16-plain-text");
+}
+
+String
+OSXClipboardUTF16Converter::doFromIClipboard(const String& data) const
+{
+ // convert and add nul terminator
+ return Unicode::UTF8ToUTF16(data);
+}
+
+String
+OSXClipboardUTF16Converter::doToIClipboard(const String& data) const
+{
+ // convert and strip nul terminator
+ return Unicode::UTF16ToUTF8(data);
+}
diff --git a/src/lib/platform/OSXClipboardUTF16Converter.h b/src/lib/platform/OSXClipboardUTF16Converter.h
new file mode 100644
index 0000000..10bb595
--- /dev/null
+++ b/src/lib/platform/OSXClipboardUTF16Converter.h
@@ -0,0 +1,37 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXClipboardAnyTextConverter.h"
+
+//! Convert to/from UTF-16 encoding
+class OSXClipboardUTF16Converter : public OSXClipboardAnyTextConverter {
+public:
+ OSXClipboardUTF16Converter();
+ virtual ~OSXClipboardUTF16Converter();
+
+ // IOSXClipboardAnyTextConverter overrides
+ virtual CFStringRef
+ getOSXFormat() const;
+
+protected:
+ // OSXClipboardAnyTextConverter overrides
+ virtual String doFromIClipboard(const String&) const;
+ virtual String doToIClipboard(const String&) const;
+};
diff --git a/src/lib/platform/OSXDragSimulator.h b/src/lib/platform/OSXDragSimulator.h
new file mode 100644
index 0000000..cb361ca
--- /dev/null
+++ b/src/lib/platform/OSXDragSimulator.h
@@ -0,0 +1,34 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#include "common/common.h"
+
+#import <CoreFoundation/CoreFoundation.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+void runCocoaApp();
+void stopCocoaLoop();
+void fakeDragging(const char* str, int cursorX, int cursorY);
+CFStringRef getCocoaDropTarget();
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/src/lib/platform/OSXDragSimulator.m b/src/lib/platform/OSXDragSimulator.m
new file mode 100644
index 0000000..affed38
--- /dev/null
+++ b/src/lib/platform/OSXDragSimulator.m
@@ -0,0 +1,102 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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.
+ */
+
+#import "platform/OSXDragSimulator.h"
+
+#import "platform/OSXDragView.h"
+
+#import <Foundation/Foundation.h>
+#import <CoreData/CoreData.h>
+#import <Cocoa/Cocoa.h>
+
+#if defined(MAC_OS_X_VERSION_10_7)
+
+NSWindow* g_dragWindow = NULL;
+OSXDragView* g_dragView = NULL;
+NSString* g_ext = NULL;
+
+void
+runCocoaApp()
+{
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ [NSApplication sharedApplication];
+
+ NSWindow* window = [[NSWindow alloc]
+ initWithContentRect: NSMakeRect(0, 0, 3, 3)
+ styleMask: NSBorderlessWindowMask
+ backing: NSBackingStoreBuffered
+ defer: NO];
+ [window setTitle: @""];
+ [window setAlphaValue:0.1];
+ [window makeKeyAndOrderFront:nil];
+
+ OSXDragView* dragView = [[OSXDragView alloc] initWithFrame:NSMakeRect(0, 0, 3, 3)];
+
+ g_dragWindow = window;
+ g_dragView = dragView;
+ [window setContentView: dragView];
+
+ NSLog(@"starting cocoa loop");
+ [NSApp run];
+
+ NSLog(@"cocoa: release");
+ [pool release];
+}
+
+void
+stopCocoaLoop()
+{
+ [NSApp stop: g_dragWindow];
+}
+
+void
+fakeDragging(const char* str, int cursorX, int cursorY)
+{
+ g_ext = [NSString stringWithUTF8String:str];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ NSRect screen = [[NSScreen mainScreen] frame];
+ NSLog ( @"screen size: witdh = %f height = %f", screen.size.width, screen.size.height);
+ NSLog ( @"mouseLocation: %d %d", cursorX, cursorY);
+
+ int newPosX = 0;
+ int newPosY = 0;
+ newPosX = cursorX - 1;
+ newPosY = screen.size.height - cursorY - 1;
+
+ NSRect rect = NSMakeRect(newPosX, newPosY, 3, 3);
+ NSLog ( @"newPosX: %d", newPosX);
+ NSLog ( @"newPosY: %d", newPosY);
+
+ [g_dragWindow setFrame:rect display:NO];
+ [g_dragWindow makeKeyAndOrderFront:nil];
+ [NSApp activateIgnoringOtherApps:YES];
+
+ [g_dragView setFileExt:g_ext];
+
+ CGEventRef down = CGEventCreateMouseEvent(CGEventSourceCreate(kCGEventSourceStateHIDSystemState), kCGEventLeftMouseDown, CGPointMake(cursorX, cursorY), kCGMouseButtonLeft);
+ CGEventPost(kCGHIDEventTap, down);
+ });
+}
+
+CFStringRef
+getCocoaDropTarget()
+{
+ // HACK: sleep, wait for cocoa drop target updated first
+ usleep(1000000);
+ return [g_dragView getDropTarget];
+}
+
+#endif
diff --git a/src/lib/platform/OSXDragView.h b/src/lib/platform/OSXDragView.h
new file mode 100644
index 0000000..9b8aa48
--- /dev/null
+++ b/src/lib/platform/OSXDragView.h
@@ -0,0 +1,34 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#import <Cocoa/Cocoa.h>
+
+#ifdef MAC_OS_X_VERSION_10_7
+
+@interface OSXDragView : NSView<NSDraggingSource,NSDraggingInfo>
+{
+ NSMutableString* m_dropTarget;
+ NSString* m_dragFileExt;
+}
+
+- (CFStringRef)getDropTarget;
+- (void)clearDropTarget;
+- (void)setFileExt:(NSString*) ext;
+
+@end
+
+#endif
diff --git a/src/lib/platform/OSXDragView.m b/src/lib/platform/OSXDragView.m
new file mode 100644
index 0000000..9f77499
--- /dev/null
+++ b/src/lib/platform/OSXDragView.m
@@ -0,0 +1,177 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#import "platform/OSXDragView.h"
+
+#ifdef MAC_OS_X_VERSION_10_7
+
+@implementation OSXDragView
+
+@dynamic draggingFormation;
+@dynamic animatesToDestination;
+@dynamic numberOfValidItemsForDrop;
+
+/* springLoadingHighlight is a property that will not be auto-synthesized by
+ clang. explicitly synthesizing it here as well as defining an empty handler
+ for resetSpringLoading() satisfies the compiler */
+@synthesize springLoadingHighlight = _springLoadingHighlight;
+
+/* unused */
+- (void)
+resetSpringLoading
+{
+}
+
+- (id)
+initWithFrame:(NSRect)frame
+{
+ self = [super initWithFrame:frame];
+ m_dropTarget = [[NSMutableString alloc] initWithCapacity:0];
+ m_dragFileExt = [[NSMutableString alloc] initWithCapacity:0];
+ return self;
+}
+
+- (void)
+drawRect:(NSRect)dirtyRect
+{
+}
+
+- (BOOL)
+acceptsFirstMouse:(NSEvent *)theEvent
+{
+ return YES;
+}
+
+- (void)
+mouseDown:(NSEvent *)theEvent
+{
+ NSLog ( @"cocoa mouse down");
+ NSPoint dragPosition;
+ NSRect imageLocation;
+ dragPosition = [self convertPoint:[theEvent locationInWindow]
+ fromView:nil];
+
+ dragPosition.x -= 16;
+ dragPosition.y -= 16;
+ imageLocation.origin = dragPosition;
+ imageLocation.size = NSMakeSize(32,32);
+ [self dragPromisedFilesOfTypes:[NSArray arrayWithObject:m_dragFileExt]
+ fromRect:imageLocation
+ source:self
+ slideBack:NO
+ event:theEvent];
+}
+
+- (NSArray*)
+namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
+{
+ [m_dropTarget setString:@""];
+ [m_dropTarget appendString:dropDestination.path];
+ NSLog ( @"cocoa drop target: %@", m_dropTarget);
+ return nil;
+}
+
+- (NSDragOperation)
+draggingSourceOperationMaskForLocal:(BOOL)flag
+{
+ return NSDragOperationCopy;
+}
+
+- (CFStringRef)
+getDropTarget
+{
+ NSMutableString* string;
+ string = [[NSMutableString alloc] initWithCapacity:0];
+ [string appendString:m_dropTarget];
+ return (CFStringRef)string;
+}
+
+- (void)
+clearDropTarget
+{
+ [m_dropTarget setString:@""];
+}
+
+- (void)
+setFileExt:(NSString*) ext
+{
+ [ext retain];
+ [m_dragFileExt release];
+ m_dragFileExt = ext;
+ NSLog(@"drag file ext: %@", m_dragFileExt);
+}
+
+- (NSWindow *)
+draggingDestinationWindow
+{
+ return nil;
+}
+
+- (NSDragOperation)
+draggingSourceOperationMask
+{
+ return NSDragOperationCopy;
+}
+
+- (NSPoint)draggingLocation
+{
+ NSPoint point;
+ return point;
+}
+
+- (NSPoint)draggedImageLocation
+{
+ NSPoint point;
+ return point;
+}
+
+- (NSImage *)draggedImage
+{
+ return nil;
+}
+
+- (NSPasteboard *)draggingPasteboard
+{
+ return nil;
+}
+
+- (id)draggingSource
+{
+ return nil;
+}
+
+- (NSInteger)draggingSequenceNumber
+{
+ return 0;
+}
+
+- (void)slideDraggedImageTo:(NSPoint)screenPoint
+{
+}
+
+- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
+{
+ return NSDragOperationCopy;
+}
+
+- (void)enumerateDraggingItemsWithOptions:(NSDraggingItemEnumerationOptions)enumOpts forView:(NSView *)view classes:(NSArray *)classArray searchOptions:(NSDictionary *)searchOptions usingBlock:(void (^)(NSDraggingItem *draggingItem, NSInteger idx, BOOL *stop))block
+{
+}
+
+@end
+
+#endif
diff --git a/src/lib/platform/OSXEventQueueBuffer.cpp b/src/lib/platform/OSXEventQueueBuffer.cpp
new file mode 100644
index 0000000..8e18afc
--- /dev/null
+++ b/src/lib/platform/OSXEventQueueBuffer.cpp
@@ -0,0 +1,143 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXEventQueueBuffer.h"
+
+#include "base/Event.h"
+#include "base/IEventQueue.h"
+
+//
+// EventQueueTimer
+//
+
+class EventQueueTimer { };
+
+//
+// OSXEventQueueBuffer
+//
+
+OSXEventQueueBuffer::OSXEventQueueBuffer(IEventQueue* events) :
+ m_event(NULL),
+ m_eventQueue(events),
+ m_carbonEventQueue(NULL)
+{
+ // do nothing
+}
+
+OSXEventQueueBuffer::~OSXEventQueueBuffer()
+{
+ // release the last event
+ if (m_event != NULL) {
+ ReleaseEvent(m_event);
+ }
+}
+
+void
+OSXEventQueueBuffer::init()
+{
+ m_carbonEventQueue = GetCurrentEventQueue();
+}
+
+void
+OSXEventQueueBuffer::waitForEvent(double timeout)
+{
+ EventRef event;
+ ReceiveNextEvent(0, NULL, timeout, false, &event);
+}
+
+IEventQueueBuffer::Type
+OSXEventQueueBuffer::getEvent(Event& event, UInt32& dataID)
+{
+ // release the previous event
+ if (m_event != NULL) {
+ ReleaseEvent(m_event);
+ m_event = NULL;
+ }
+
+ // get the next event
+ OSStatus error = ReceiveNextEvent(0, NULL, 0.0, true, &m_event);
+
+ // handle the event
+ if (error == eventLoopQuitErr) {
+ event = Event(Event::kQuit);
+ return kSystem;
+ }
+ else if (error != noErr) {
+ return kNone;
+ }
+ else {
+ UInt32 eventClass = GetEventClass(m_event);
+ switch (eventClass) {
+ case 'Syne':
+ dataID = GetEventKind(m_event);
+ return kUser;
+
+ default:
+ event = Event(Event::kSystem,
+ m_eventQueue->getSystemTarget(), &m_event);
+ return kSystem;
+ }
+ }
+}
+
+bool
+OSXEventQueueBuffer::addEvent(UInt32 dataID)
+{
+ EventRef event;
+ OSStatus error = CreateEvent(
+ kCFAllocatorDefault,
+ 'Syne',
+ dataID,
+ 0,
+ kEventAttributeNone,
+ &event);
+
+ if (error == noErr) {
+
+ assert(m_carbonEventQueue != NULL);
+
+ error = PostEventToQueue(
+ m_carbonEventQueue,
+ event,
+ kEventPriorityStandard);
+
+ ReleaseEvent(event);
+ }
+
+ return (error == noErr);
+}
+
+bool
+OSXEventQueueBuffer::isEmpty() const
+{
+ EventRef event;
+ OSStatus status = ReceiveNextEvent(0, NULL, 0.0, false, &event);
+ return (status == eventLoopTimedOutErr);
+}
+
+EventQueueTimer*
+OSXEventQueueBuffer::newTimer(double, bool) const
+{
+ return new EventQueueTimer;
+}
+
+void
+OSXEventQueueBuffer::deleteTimer(EventQueueTimer* timer) const
+{
+ delete timer;
+}
diff --git a/src/lib/platform/OSXEventQueueBuffer.h b/src/lib/platform/OSXEventQueueBuffer.h
new file mode 100644
index 0000000..28c4a5d
--- /dev/null
+++ b/src/lib/platform/OSXEventQueueBuffer.h
@@ -0,0 +1,47 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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/IEventQueueBuffer.h"
+
+#include <Carbon/Carbon.h>
+
+class IEventQueue;
+
+//! Event queue buffer for OS X
+class OSXEventQueueBuffer : public IEventQueueBuffer {
+public:
+ OSXEventQueueBuffer(IEventQueue* eventQueue);
+ virtual ~OSXEventQueueBuffer();
+
+ // IEventQueueBuffer overrides
+ virtual void init();
+ virtual void waitForEvent(double timeout);
+ virtual Type getEvent(Event& event, UInt32& dataID);
+ virtual bool addEvent(UInt32 dataID);
+ virtual bool isEmpty() const;
+ virtual EventQueueTimer*
+ newTimer(double duration, bool oneShot) const;
+ virtual void deleteTimer(EventQueueTimer*) const;
+
+private:
+ EventRef m_event;
+ IEventQueue* m_eventQueue;
+ EventQueueRef m_carbonEventQueue;
+};
diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp
new file mode 100644
index 0000000..482d7c1
--- /dev/null
+++ b/src/lib/platform/OSXKeyState.cpp
@@ -0,0 +1,912 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXKeyState.h"
+#include "platform/OSXUchrKeyResource.h"
+#include "platform/OSXMediaKeySupport.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+
+#include <Carbon/Carbon.h>
+#include <IOKit/hidsystem/IOHIDLib.h>
+
+// Note that some virtual keys codes appear more than once. The
+// first instance of a virtual key code maps to the KeyID that we
+// want to generate for that code. The others are for mapping
+// different KeyIDs to a single key code.
+static const UInt32 s_shiftVK = kVK_Shift;
+static const UInt32 s_controlVK = kVK_Control;
+static const UInt32 s_altVK = kVK_Option;
+static const UInt32 s_superVK = kVK_Command;
+static const UInt32 s_capsLockVK = kVK_CapsLock;
+static const UInt32 s_numLockVK = kVK_ANSI_KeypadClear; // 71
+
+static const UInt32 s_brightnessUp = 144;
+static const UInt32 s_brightnessDown = 145;
+static const UInt32 s_missionControlVK = 160;
+static const UInt32 s_launchpadVK = 131;
+
+static const UInt32 s_osxNumLock = 1 << 16;
+
+struct KeyEntry {
+public:
+ KeyID m_keyID;
+ UInt32 m_virtualKey;
+};
+static const KeyEntry s_controlKeys[] = {
+ // cursor keys. if we don't do this we'll may still get these from
+ // the keyboard resource but they may not correspond to the arrow
+ // keys.
+ { kKeyLeft, kVK_LeftArrow },
+ { kKeyRight, kVK_RightArrow },
+ { kKeyUp, kVK_UpArrow },
+ { kKeyDown, kVK_DownArrow },
+ { kKeyHome, kVK_Home },
+ { kKeyEnd, kVK_End },
+ { kKeyPageUp, kVK_PageUp },
+ { kKeyPageDown, kVK_PageDown },
+ { kKeyInsert, kVK_Help }, // Mac Keyboards have 'Help' on 'Insert'
+
+ // function keys
+ { kKeyF1, kVK_F1 },
+ { kKeyF2, kVK_F2 },
+ { kKeyF3, kVK_F3 },
+ { kKeyF4, kVK_F4 },
+ { kKeyF5, kVK_F5 },
+ { kKeyF6, kVK_F6 },
+ { kKeyF7, kVK_F7 },
+ { kKeyF8, kVK_F8 },
+ { kKeyF9, kVK_F9 },
+ { kKeyF10, kVK_F10 },
+ { kKeyF11, kVK_F11 },
+ { kKeyF12, kVK_F12 },
+ { kKeyF13, kVK_F13 },
+ { kKeyF14, kVK_F14 },
+ { kKeyF15, kVK_F15 },
+ { kKeyF16, kVK_F16 },
+
+ { kKeyKP_0, kVK_ANSI_Keypad0 },
+ { kKeyKP_1, kVK_ANSI_Keypad1 },
+ { kKeyKP_2, kVK_ANSI_Keypad2 },
+ { kKeyKP_3, kVK_ANSI_Keypad3 },
+ { kKeyKP_4, kVK_ANSI_Keypad4 },
+ { kKeyKP_5, kVK_ANSI_Keypad5 },
+ { kKeyKP_6, kVK_ANSI_Keypad6 },
+ { kKeyKP_7, kVK_ANSI_Keypad7 },
+ { kKeyKP_8, kVK_ANSI_Keypad8 },
+ { kKeyKP_9, kVK_ANSI_Keypad9 },
+ { kKeyKP_Decimal, kVK_ANSI_KeypadDecimal },
+ { kKeyKP_Equal, kVK_ANSI_KeypadEquals },
+ { kKeyKP_Multiply, kVK_ANSI_KeypadMultiply },
+ { kKeyKP_Add, kVK_ANSI_KeypadPlus },
+ { kKeyKP_Divide, kVK_ANSI_KeypadDivide },
+ { kKeyKP_Subtract, kVK_ANSI_KeypadMinus },
+ { kKeyKP_Enter, kVK_ANSI_KeypadEnter },
+
+ // virtual key 110 is fn+enter and i have no idea what that's supposed
+ // to map to. also the enter key with numlock on is a modifier but i
+ // don't know which.
+
+ // modifier keys. OS X doesn't seem to support right handed versions
+ // of modifier keys so we map them to the left handed versions.
+ { kKeyShift_L, s_shiftVK },
+ { kKeyShift_R, s_shiftVK }, // 60
+ { kKeyControl_L, s_controlVK },
+ { kKeyControl_R, s_controlVK }, // 62
+ { kKeyAlt_L, s_altVK },
+ { kKeyAlt_R, s_altVK },
+ { kKeySuper_L, s_superVK },
+ { kKeySuper_R, s_superVK }, // 61
+ { kKeyMeta_L, s_superVK },
+ { kKeyMeta_R, s_superVK }, // 61
+
+ // toggle modifiers
+ { kKeyNumLock, s_numLockVK },
+ { kKeyCapsLock, s_capsLockVK },
+
+ { kKeyMissionControl, s_missionControlVK },
+ { kKeyLaunchpad, s_launchpadVK },
+ { kKeyBrightnessUp, s_brightnessUp },
+ { kKeyBrightnessDown, s_brightnessDown }
+};
+
+
+//
+// OSXKeyState
+//
+
+OSXKeyState::OSXKeyState(IEventQueue* events) :
+ KeyState(events)
+{
+ init();
+}
+
+OSXKeyState::OSXKeyState(IEventQueue* events, barrier::KeyMap& keyMap) :
+ KeyState(events, keyMap)
+{
+ init();
+}
+
+OSXKeyState::~OSXKeyState()
+{
+}
+
+void
+OSXKeyState::init()
+{
+ m_deadKeyState = 0;
+ m_shiftPressed = false;
+ m_controlPressed = false;
+ m_altPressed = false;
+ m_superPressed = false;
+ m_capsPressed = false;
+
+ // build virtual key map
+ for (size_t i = 0; i < sizeof(s_controlKeys) / sizeof(s_controlKeys[0]);
+ ++i) {
+
+ m_virtualKeyMap[s_controlKeys[i].m_virtualKey] =
+ s_controlKeys[i].m_keyID;
+ }
+}
+
+KeyModifierMask
+OSXKeyState::mapModifiersFromOSX(UInt32 mask) const
+{
+ KeyModifierMask outMask = 0;
+ if ((mask & kCGEventFlagMaskShift) != 0) {
+ outMask |= KeyModifierShift;
+ }
+ if ((mask & kCGEventFlagMaskControl) != 0) {
+ outMask |= KeyModifierControl;
+ }
+ if ((mask & kCGEventFlagMaskAlternate) != 0) {
+ outMask |= KeyModifierAlt;
+ }
+ if ((mask & kCGEventFlagMaskCommand) != 0) {
+ outMask |= KeyModifierSuper;
+ }
+ if ((mask & kCGEventFlagMaskAlphaShift) != 0) {
+ outMask |= KeyModifierCapsLock;
+ }
+ if ((mask & kCGEventFlagMaskNumericPad) != 0) {
+ outMask |= KeyModifierNumLock;
+ }
+
+ LOG((CLOG_DEBUG1 "mask=%04x outMask=%04x", mask, outMask));
+ return outMask;
+}
+
+KeyModifierMask
+OSXKeyState::mapModifiersToCarbon(UInt32 mask) const
+{
+ KeyModifierMask outMask = 0;
+ if ((mask & kCGEventFlagMaskShift) != 0) {
+ outMask |= shiftKey;
+ }
+ if ((mask & kCGEventFlagMaskControl) != 0) {
+ outMask |= controlKey;
+ }
+ if ((mask & kCGEventFlagMaskCommand) != 0) {
+ outMask |= cmdKey;
+ }
+ if ((mask & kCGEventFlagMaskAlternate) != 0) {
+ outMask |= optionKey;
+ }
+ if ((mask & kCGEventFlagMaskAlphaShift) != 0) {
+ outMask |= alphaLock;
+ }
+ if ((mask & kCGEventFlagMaskNumericPad) != 0) {
+ outMask |= s_osxNumLock;
+ }
+
+ return outMask;
+}
+
+KeyButton
+OSXKeyState::mapKeyFromEvent(KeyIDs& ids,
+ KeyModifierMask* maskOut, CGEventRef event) const
+{
+ ids.clear();
+
+ // map modifier key
+ if (maskOut != NULL) {
+ KeyModifierMask activeMask = getActiveModifiers();
+ activeMask &= ~KeyModifierAltGr;
+ *maskOut = activeMask;
+ }
+
+ // get virtual key
+ UInt32 vkCode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
+
+ // handle up events
+ UInt32 eventKind = CGEventGetType(event);
+ if (eventKind == kCGEventKeyUp) {
+ // the id isn't used. we just need the same button we used on
+ // the key press. note that we don't use or reset the dead key
+ // state; up events should not affect the dead key state.
+ ids.push_back(kKeyNone);
+ return mapVirtualKeyToKeyButton(vkCode);
+ }
+
+ // check for special keys
+ VirtualKeyMap::const_iterator i = m_virtualKeyMap.find(vkCode);
+ if (i != m_virtualKeyMap.end()) {
+ m_deadKeyState = 0;
+ ids.push_back(i->second);
+ return mapVirtualKeyToKeyButton(vkCode);
+ }
+
+ // get keyboard info
+ TISInputSourceRef currentKeyboardLayout = TISCopyCurrentKeyboardLayoutInputSource();
+
+ if (currentKeyboardLayout == NULL) {
+ return kKeyNone;
+ }
+
+ // get the event modifiers and remove the command and control
+ // keys. note if we used them though.
+ // UCKeyTranslate expects old-style Carbon modifiers, so convert.
+ UInt32 modifiers;
+ modifiers = mapModifiersToCarbon(CGEventGetFlags(event));
+ static const UInt32 s_commandModifiers =
+ cmdKey | controlKey | rightControlKey;
+ bool isCommand = ((modifiers & s_commandModifiers) != 0);
+ modifiers &= ~s_commandModifiers;
+
+ // if we've used a command key then we want the glyph produced without
+ // the option key (i.e. the base glyph).
+ //if (isCommand) {
+ modifiers &= ~optionKey;
+ //}
+
+ // choose action
+ UInt16 action;
+ if (eventKind==kCGEventKeyDown) {
+ action = kUCKeyActionDown;
+ }
+ else if (CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat)==1) {
+ action = kUCKeyActionAutoKey;
+ }
+ else {
+ return 0;
+ }
+
+ // translate via uchr resource
+ CFDataRef ref = (CFDataRef) TISGetInputSourceProperty(currentKeyboardLayout,
+ kTISPropertyUnicodeKeyLayoutData);
+ const UCKeyboardLayout* layout = (const UCKeyboardLayout*) CFDataGetBytePtr(ref);
+ const bool layoutValid = (layout != NULL);
+
+ if (layoutValid) {
+ // translate key
+ UniCharCount count;
+ UniChar chars[2];
+ LOG((CLOG_DEBUG2 "modifiers: %08x", modifiers & 0xffu));
+ OSStatus status = UCKeyTranslate(layout,
+ vkCode & 0xffu, action,
+ (modifiers >> 8) & 0xffu,
+ LMGetKbdType(), 0, &m_deadKeyState,
+ sizeof(chars) / sizeof(chars[0]), &count, chars);
+
+ // get the characters
+ if (status == 0) {
+ if (count != 0 || m_deadKeyState == 0) {
+ m_deadKeyState = 0;
+ for (UniCharCount i = 0; i < count; ++i) {
+ ids.push_back(IOSXKeyResource::unicharToKeyID(chars[i]));
+ }
+ adjustAltGrModifier(ids, maskOut, isCommand);
+ return mapVirtualKeyToKeyButton(vkCode);
+ }
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+bool
+OSXKeyState::fakeCtrlAltDel()
+{
+ // pass keys through unchanged
+ return false;
+}
+
+bool
+OSXKeyState::fakeMediaKey(KeyID id)
+{
+ return fakeNativeMediaKey(id);;
+}
+
+CGEventFlags
+OSXKeyState::getModifierStateAsOSXFlags()
+{
+ CGEventFlags modifiers = 0;
+
+ if (m_shiftPressed) {
+ modifiers |= kCGEventFlagMaskShift;
+ }
+
+ if (m_controlPressed) {
+ modifiers |= kCGEventFlagMaskControl;
+ }
+
+ if (m_altPressed) {
+ modifiers |= kCGEventFlagMaskAlternate;
+ }
+
+ if (m_superPressed) {
+ modifiers |= kCGEventFlagMaskCommand;
+ }
+
+ if (m_capsPressed) {
+ modifiers |= kCGEventFlagMaskAlphaShift;
+ }
+
+ return modifiers;
+}
+
+KeyModifierMask
+OSXKeyState::pollActiveModifiers() const
+{
+ // falsely assumed that the mask returned by GetCurrentKeyModifiers()
+ // was the same as a CGEventFlags (which is what mapModifiersFromOSX
+ // expects). patch by Marc
+ UInt32 mask = GetCurrentKeyModifiers();
+ KeyModifierMask outMask = 0;
+
+ if ((mask & shiftKey) != 0) {
+ outMask |= KeyModifierShift;
+ }
+ if ((mask & controlKey) != 0) {
+ outMask |= KeyModifierControl;
+ }
+ if ((mask & optionKey) != 0) {
+ outMask |= KeyModifierAlt;
+ }
+ if ((mask & cmdKey) != 0) {
+ outMask |= KeyModifierSuper;
+ }
+ if ((mask & alphaLock) != 0) {
+ outMask |= KeyModifierCapsLock;
+ }
+ if ((mask & s_osxNumLock) != 0) {
+ outMask |= KeyModifierNumLock;
+ }
+
+ LOG((CLOG_DEBUG1 "mask=%04x outMask=%04x", mask, outMask));
+ return outMask;
+}
+
+SInt32
+OSXKeyState::pollActiveGroup() const
+{
+ TISInputSourceRef keyboardLayout = TISCopyCurrentKeyboardLayoutInputSource();
+ CFDataRef id = (CFDataRef)TISGetInputSourceProperty(
+ keyboardLayout, kTISPropertyInputSourceID);
+
+ GroupMap::const_iterator i = m_groupMap.find(id);
+ if (i != m_groupMap.end()) {
+ return i->second;
+ }
+
+ LOG((CLOG_DEBUG "can't get the active group, use the first group instead"));
+
+ return 0;
+}
+
+void
+OSXKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const
+{
+ ::KeyMap km;
+ GetKeys(km);
+ const UInt8* m = reinterpret_cast<const UInt8*>(km);
+ for (UInt32 i = 0; i < 16; ++i) {
+ for (UInt32 j = 0; j < 8; ++j) {
+ if ((m[i] & (1u << j)) != 0) {
+ pressedKeys.insert(mapVirtualKeyToKeyButton(8 * i + j));
+ }
+ }
+ }
+}
+
+void
+OSXKeyState::getKeyMap(barrier::KeyMap& keyMap)
+{
+ // update keyboard groups
+ if (getGroups(m_groups)) {
+ m_groupMap.clear();
+ SInt32 numGroups = (SInt32)m_groups.size();
+ for (SInt32 g = 0; g < numGroups; ++g) {
+ CFDataRef id = (CFDataRef)TISGetInputSourceProperty(
+ m_groups[g], kTISPropertyInputSourceID);
+ m_groupMap[id] = g;
+ }
+ }
+
+ UInt32 keyboardType = LMGetKbdType();
+ for (SInt32 g = 0, n = (SInt32)m_groups.size(); g < n; ++g) {
+ // add special keys
+ getKeyMapForSpecialKeys(keyMap, g);
+
+ const void* resource;
+ bool layoutValid = false;
+
+ // add regular keys
+ // try uchr resource first
+ CFDataRef resourceRef = (CFDataRef)TISGetInputSourceProperty(
+ m_groups[g], kTISPropertyUnicodeKeyLayoutData);
+
+ layoutValid = resourceRef != NULL;
+ if (layoutValid)
+ resource = CFDataGetBytePtr(resourceRef);
+
+ if (layoutValid) {
+ OSXUchrKeyResource uchr(resource, keyboardType);
+ if (uchr.isValid()) {
+ LOG((CLOG_DEBUG1 "using uchr resource for group %d", g));
+ getKeyMap(keyMap, g, uchr);
+ continue;
+ }
+ }
+
+ LOG((CLOG_DEBUG1 "no keyboard resource for group %d", g));
+ }
+}
+
+static io_connect_t getEventDriver(void)
+{
+ static mach_port_t sEventDrvrRef = 0;
+ mach_port_t masterPort, service, iter;
+ kern_return_t kr;
+
+ if (!sEventDrvrRef) {
+ // Get master device port
+ kr = IOMasterPort(bootstrap_port, &masterPort);
+ assert(KERN_SUCCESS == kr);
+
+ kr = IOServiceGetMatchingServices(masterPort,
+ IOServiceMatching(kIOHIDSystemClass), &iter);
+ assert(KERN_SUCCESS == kr);
+
+ service = IOIteratorNext(iter);
+ assert(service);
+
+ kr = IOServiceOpen(service, mach_task_self(),
+ kIOHIDParamConnectType, &sEventDrvrRef);
+ assert(KERN_SUCCESS == kr);
+
+ IOObjectRelease(service);
+ IOObjectRelease(iter);
+ }
+
+ return sEventDrvrRef;
+}
+
+void
+OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode,
+ const bool postDown)
+{
+ static UInt32 modifiers = 0;
+
+ NXEventData event;
+ IOGPoint loc = { 0, 0 };
+ UInt32 modifiersDelta = 0;
+
+ bzero(&event, sizeof(NXEventData));
+
+ switch (virtualKeyCode)
+ {
+ case s_shiftVK:
+ case s_superVK:
+ case s_altVK:
+ case s_controlVK:
+ case s_capsLockVK:
+ switch (virtualKeyCode)
+ {
+ case s_shiftVK:
+ modifiersDelta = NX_SHIFTMASK;
+ m_shiftPressed = postDown;
+ break;
+ case s_superVK:
+ modifiersDelta = NX_COMMANDMASK;
+ m_superPressed = postDown;
+ break;
+ case s_altVK:
+ modifiersDelta = NX_ALTERNATEMASK;
+ m_altPressed = postDown;
+ break;
+ case s_controlVK:
+ modifiersDelta = NX_CONTROLMASK;
+ m_controlPressed = postDown;
+ break;
+ case s_capsLockVK:
+ modifiersDelta = NX_ALPHASHIFTMASK;
+ m_capsPressed = postDown;
+ break;
+ }
+
+ // update the modifier bit
+ if (postDown) {
+ modifiers |= modifiersDelta;
+ }
+ else {
+ modifiers &= ~modifiersDelta;
+ }
+
+ kern_return_t kr;
+ kr = IOHIDPostEvent(getEventDriver(), NX_FLAGSCHANGED, loc,
+ &event, kNXEventDataVersion, modifiers, true);
+ assert(KERN_SUCCESS == kr);
+ break;
+
+ default:
+ event.key.repeat = false;
+ event.key.keyCode = virtualKeyCode;
+ event.key.origCharSet = event.key.charSet = NX_ASCIISET;
+ event.key.origCharCode = event.key.charCode = 0;
+ kr = IOHIDPostEvent(getEventDriver(),
+ postDown ? NX_KEYDOWN : NX_KEYUP,
+ loc, &event, kNXEventDataVersion, 0, false);
+ assert(KERN_SUCCESS == kr);
+ break;
+ }
+}
+
+void
+OSXKeyState::fakeKey(const Keystroke& keystroke)
+{
+ switch (keystroke.m_type) {
+ case Keystroke::kButton: {
+
+ KeyButton button = keystroke.m_data.m_button.m_button;
+ bool keyDown = keystroke.m_data.m_button.m_press;
+ CGKeyCode virtualKey = mapKeyButtonToVirtualKey(button);
+
+ LOG((CLOG_DEBUG1
+ " button=0x%04x virtualKey=0x%04x keyDown=%s",
+ button, virtualKey, keyDown ? "down" : "up"));
+
+ postHIDVirtualKey(virtualKey, keyDown);
+
+ break;
+ }
+
+ 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));
+ }
+ break;
+ }
+ }
+}
+
+void
+OSXKeyState::getKeyMapForSpecialKeys(barrier::KeyMap& keyMap, SInt32 group) const
+{
+ // special keys are insensitive to modifers and none are dead keys
+ barrier::KeyMap::KeyItem item;
+ for (size_t i = 0; i < sizeof(s_controlKeys) /
+ sizeof(s_controlKeys[0]); ++i) {
+ const KeyEntry& entry = s_controlKeys[i];
+ item.m_id = entry.m_keyID;
+ item.m_group = group;
+ item.m_button = mapVirtualKeyToKeyButton(entry.m_virtualKey);
+ item.m_required = 0;
+ item.m_sensitive = 0;
+ item.m_dead = false;
+ item.m_client = 0;
+ barrier::KeyMap::initModifierKey(item);
+ keyMap.addKeyEntry(item);
+
+ if (item.m_lock) {
+ // all locking keys are half duplex on OS X
+ keyMap.addHalfDuplexButton(item.m_button);
+ }
+ }
+
+ // note: we don't special case the number pad keys. querying the
+ // mac keyboard returns the non-keypad version of those keys but
+ // a KeyState always provides a mapping from keypad keys to
+ // non-keypad keys so we'll be able to generate the characters
+ // anyway.
+}
+
+bool
+OSXKeyState::getKeyMap(barrier::KeyMap& keyMap,
+ SInt32 group, const IOSXKeyResource& r) const
+{
+ if (!r.isValid()) {
+ return false;
+ }
+
+ // space for all possible modifier combinations
+ std::vector<bool> modifiers(r.getNumModifierCombinations());
+
+ // make space for the keys that any single button can synthesize
+ std::vector<std::pair<KeyID, bool> > buttonKeys(r.getNumTables());
+
+ // iterate over each button
+ barrier::KeyMap::KeyItem item;
+ for (UInt32 i = 0; i < r.getNumButtons(); ++i) {
+ item.m_button = mapVirtualKeyToKeyButton(i);
+
+ // the KeyIDs we've already handled
+ std::set<KeyID> keys;
+
+ // convert the entry in each table for this button to a KeyID
+ for (UInt32 j = 0; j < r.getNumTables(); ++j) {
+ buttonKeys[j].first = r.getKey(j, i);
+ buttonKeys[j].second = barrier::KeyMap::isDeadKey(buttonKeys[j].first);
+ }
+
+ // iterate over each character table
+ for (UInt32 j = 0; j < r.getNumTables(); ++j) {
+ // get the KeyID for the button/table
+ KeyID id = buttonKeys[j].first;
+ if (id == kKeyNone) {
+ continue;
+ }
+
+ // if we've already handled the KeyID in the table then
+ // move on to the next table
+ if (keys.count(id) > 0) {
+ continue;
+ }
+ keys.insert(id);
+
+ // prepare item. the client state is 1 for dead keys.
+ item.m_id = id;
+ item.m_group = group;
+ item.m_dead = buttonKeys[j].second;
+ item.m_client = buttonKeys[j].second ? 1 : 0;
+ barrier::KeyMap::initModifierKey(item);
+ if (item.m_lock) {
+ // all locking keys are half duplex on OS X
+ keyMap.addHalfDuplexButton(i);
+ }
+
+ // collect the tables that map to the same KeyID. we know it
+ // can't be any earlier tables because of the check above.
+ std::set<UInt8> tables;
+ tables.insert(static_cast<UInt8>(j));
+ for (UInt32 k = j + 1; k < r.getNumTables(); ++k) {
+ if (buttonKeys[k].first == id) {
+ tables.insert(static_cast<UInt8>(k));
+ }
+ }
+
+ // collect the modifier combinations that map to any of the
+ // tables we just collected
+ for (UInt32 k = 0; k < r.getNumModifierCombinations(); ++k) {
+ modifiers[k] = (tables.count(r.getTableForModifier(k)) > 0);
+ }
+
+ // figure out which modifiers the key is sensitive to. the
+ // key is insensitive to a modifier if for every modifier mask
+ // with the modifier bit unset in the modifiers we also find
+ // the same mask with the bit set.
+ //
+ // we ignore a few modifiers that we know aren't important
+ // for generating characters. in fact, we want to ignore any
+ // characters generated by the control key. we don't map
+ // those and instead expect the control modifier plus a key.
+ UInt32 sensitive = 0;
+ for (UInt32 k = 0; (1u << k) <
+ r.getNumModifierCombinations(); ++k) {
+ UInt32 bit = (1u << k);
+ if ((bit << 8) == cmdKey ||
+ (bit << 8) == controlKey ||
+ (bit << 8) == rightControlKey) {
+ continue;
+ }
+ for (UInt32 m = 0; m < r.getNumModifierCombinations(); ++m) {
+ if (modifiers[m] != modifiers[m ^ bit]) {
+ sensitive |= bit;
+ break;
+ }
+ }
+ }
+
+ // find each required modifier mask. the key can be synthesized
+ // using any of the masks.
+ std::set<UInt32> required;
+ for (UInt32 k = 0; k < r.getNumModifierCombinations(); ++k) {
+ if ((k & sensitive) == k && modifiers[k & sensitive]) {
+ required.insert(k);
+ }
+ }
+
+ // now add a key entry for each key/required modifier pair.
+ item.m_sensitive = mapModifiersFromOSX(sensitive << 16);
+ for (std::set<UInt32>::iterator k = required.begin();
+ k != required.end(); ++k) {
+ item.m_required = mapModifiersFromOSX(*k << 16);
+ keyMap.addKeyEntry(item);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+OSXKeyState::mapBarrierHotKeyToMac(KeyID key, KeyModifierMask mask,
+ UInt32 &macVirtualKey, UInt32 &macModifierMask) const
+{
+ // look up button for key
+ KeyButton button = getButton(key, pollActiveGroup());
+ if (button == 0 && key != kKeyNone) {
+ return false;
+ }
+ macVirtualKey = mapKeyButtonToVirtualKey(button);
+
+ // calculate modifier mask
+ macModifierMask = 0;
+ if ((mask & KeyModifierShift) != 0) {
+ macModifierMask |= shiftKey;
+ }
+ if ((mask & KeyModifierControl) != 0) {
+ macModifierMask |= controlKey;
+ }
+ if ((mask & KeyModifierAlt) != 0) {
+ macModifierMask |= cmdKey;
+ }
+ if ((mask & KeyModifierSuper) != 0) {
+ macModifierMask |= optionKey;
+ }
+ if ((mask & KeyModifierCapsLock) != 0) {
+ macModifierMask |= alphaLock;
+ }
+ if ((mask & KeyModifierNumLock) != 0) {
+ macModifierMask |= s_osxNumLock;
+ }
+
+ return true;
+}
+
+void
+OSXKeyState::handleModifierKeys(void* target,
+ KeyModifierMask oldMask, KeyModifierMask newMask)
+{
+ // compute changed modifiers
+ KeyModifierMask changed = (oldMask ^ newMask);
+
+ // synthesize changed modifier keys
+ if ((changed & KeyModifierShift) != 0) {
+ handleModifierKey(target, s_shiftVK, kKeyShift_L,
+ (newMask & KeyModifierShift) != 0, newMask);
+ }
+ if ((changed & KeyModifierControl) != 0) {
+ handleModifierKey(target, s_controlVK, kKeyControl_L,
+ (newMask & KeyModifierControl) != 0, newMask);
+ }
+ if ((changed & KeyModifierAlt) != 0) {
+ handleModifierKey(target, s_altVK, kKeyAlt_L,
+ (newMask & KeyModifierAlt) != 0, newMask);
+ }
+ if ((changed & KeyModifierSuper) != 0) {
+ handleModifierKey(target, s_superVK, kKeySuper_L,
+ (newMask & KeyModifierSuper) != 0, newMask);
+ }
+ if ((changed & KeyModifierCapsLock) != 0) {
+ handleModifierKey(target, s_capsLockVK, kKeyCapsLock,
+ (newMask & KeyModifierCapsLock) != 0, newMask);
+ }
+ if ((changed & KeyModifierNumLock) != 0) {
+ handleModifierKey(target, s_numLockVK, kKeyNumLock,
+ (newMask & KeyModifierNumLock) != 0, newMask);
+ }
+}
+
+void
+OSXKeyState::handleModifierKey(void* target,
+ UInt32 virtualKey, KeyID id,
+ bool down, KeyModifierMask newMask)
+{
+ KeyButton button = mapVirtualKeyToKeyButton(virtualKey);
+ onKey(button, down, newMask);
+ sendKeyEvent(target, down, false, id, newMask, 0, button);
+}
+
+bool
+OSXKeyState::getGroups(GroupList& groups) const
+{
+ CFIndex n;
+ bool gotLayouts = false;
+
+ // get number of layouts
+ CFStringRef keys[] = { kTISPropertyInputSourceCategory };
+ CFStringRef values[] = { kTISCategoryKeyboardInputSource };
+ CFDictionaryRef dict = CFDictionaryCreate(NULL, (const void **)keys, (const void **)values, 1, NULL, NULL);
+ CFArrayRef kbds = TISCreateInputSourceList(dict, false);
+ n = CFArrayGetCount(kbds);
+ gotLayouts = (n != 0);
+
+ if (!gotLayouts) {
+ LOG((CLOG_DEBUG1 "can't get keyboard layouts"));
+ return false;
+ }
+
+ // get each layout
+ groups.clear();
+ for (CFIndex i = 0; i < n; ++i) {
+ bool addToGroups = true;
+ TISInputSourceRef keyboardLayout =
+ (TISInputSourceRef)CFArrayGetValueAtIndex(kbds, i);
+
+ if (addToGroups)
+ groups.push_back(keyboardLayout);
+ }
+ return true;
+}
+
+void
+OSXKeyState::setGroup(SInt32 group)
+{
+ TISSetInputMethodKeyboardLayoutOverride(m_groups[group]);
+}
+
+void
+OSXKeyState::checkKeyboardLayout()
+{
+ // XXX -- should call this when notified that groups have changed.
+ // if no notification for that then we should poll.
+ GroupList groups;
+ if (getGroups(groups) && groups != m_groups) {
+ updateKeyMap();
+ updateKeyState();
+ }
+}
+
+void
+OSXKeyState::adjustAltGrModifier(const KeyIDs& ids,
+ KeyModifierMask* mask, bool isCommand) const
+{
+ if (!isCommand) {
+ for (KeyIDs::const_iterator i = ids.begin(); i != ids.end(); ++i) {
+ KeyID id = *i;
+ if (id != kKeyNone &&
+ ((id < 0xe000u || id > 0xefffu) ||
+ (id >= kKeyKP_Equal && id <= kKeyKP_9))) {
+ *mask |= KeyModifierAltGr;
+ return;
+ }
+ }
+ }
+}
+
+KeyButton
+OSXKeyState::mapVirtualKeyToKeyButton(UInt32 keyCode)
+{
+ // 'A' maps to 0 so shift every id
+ return static_cast<KeyButton>(keyCode + KeyButtonOffset);
+}
+
+UInt32
+OSXKeyState::mapKeyButtonToVirtualKey(KeyButton keyButton)
+{
+ return static_cast<UInt32>(keyButton - KeyButtonOffset);
+}
diff --git a/src/lib/platform/OSXKeyState.h b/src/lib/platform/OSXKeyState.h
new file mode 100644
index 0000000..4d92860
--- /dev/null
+++ b/src/lib/platform/OSXKeyState.h
@@ -0,0 +1,180 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "barrier/KeyState.h"
+#include "common/stdmap.h"
+#include "common/stdset.h"
+#include "common/stdvector.h"
+
+#include <Carbon/Carbon.h>
+
+typedef TISInputSourceRef KeyLayout;
+class IOSXKeyResource;
+
+//! OS X key state
+/*!
+A key state for OS X.
+*/
+class OSXKeyState : public KeyState {
+public:
+ typedef std::vector<KeyID> KeyIDs;
+
+ OSXKeyState(IEventQueue* events);
+ OSXKeyState(IEventQueue* events, barrier::KeyMap& keyMap);
+ virtual ~OSXKeyState();
+
+ //! @name modifiers
+ //@{
+
+ //! Handle modifier key change
+ /*!
+ Determines which modifier keys have changed and updates the modifier
+ state and sends key events as appropriate.
+ */
+ void handleModifierKeys(void* target,
+ KeyModifierMask oldMask, KeyModifierMask newMask);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Convert OS X modifier mask to barrier mask
+ /*!
+ Returns the barrier modifier mask corresponding to the OS X modifier
+ mask in \p mask.
+ */
+ KeyModifierMask mapModifiersFromOSX(UInt32 mask) const;
+
+ //! Convert CG flags-style modifier mask to old-style Carbon
+ /*!
+ Still required in a few places for translation calls.
+ */
+ KeyModifierMask mapModifiersToCarbon(UInt32 mask) const;
+
+ //! Map key event to keys
+ /*!
+ Converts a key event into a sequence of KeyIDs and the shadow modifier
+ state to a modifier mask. The KeyIDs list, in order, the characters
+ generated by the key press/release. It returns the id of the button
+ that was pressed or released, or 0 if the button doesn't map to a known
+ KeyID.
+ */
+ KeyButton mapKeyFromEvent(KeyIDs& ids,
+ KeyModifierMask* maskOut, CGEventRef event) const;
+
+ //! Map key and mask to native values
+ /*!
+ Calculates mac virtual key and mask for a key \p key and modifiers
+ \p mask. Returns \c true if the key can be mapped, \c false otherwise.
+ */
+ bool mapBarrierHotKeyToMac(KeyID key, KeyModifierMask mask,
+ UInt32& macVirtualKey,
+ UInt32& macModifierMask) const;
+
+ //@}
+
+ // IKeyState overrides
+ virtual bool fakeCtrlAltDel();
+ virtual bool fakeMediaKey(KeyID id);
+ virtual KeyModifierMask
+ pollActiveModifiers() const;
+ virtual SInt32 pollActiveGroup() const;
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const;
+
+ CGEventFlags getModifierStateAsOSXFlags();
+protected:
+ // KeyState overrides
+ virtual void getKeyMap(barrier::KeyMap& keyMap);
+ virtual void fakeKey(const Keystroke& keystroke);
+
+private:
+ class KeyResource;
+ typedef std::vector<KeyLayout> GroupList;
+
+ // Add hard coded special keys to a barrier::KeyMap.
+ void getKeyMapForSpecialKeys(
+ barrier::KeyMap& keyMap, SInt32 group) const;
+
+ // Convert keyboard resource to a key map
+ bool getKeyMap(barrier::KeyMap& keyMap,
+ SInt32 group, const IOSXKeyResource& r) const;
+
+ // Get the available keyboard groups
+ bool getGroups(GroupList&) const;
+
+ // Change active keyboard group to group
+ void setGroup(SInt32 group);
+
+ // Check if the keyboard layout has changed and update keyboard state
+ // if so.
+ void checkKeyboardLayout();
+
+ // Send an event for the given modifier key
+ void handleModifierKey(void* target,
+ UInt32 virtualKey, KeyID id,
+ bool down, KeyModifierMask newMask);
+
+ // Checks if any in \p ids is a glyph key and if \p isCommand is false.
+ // If so it adds the AltGr modifier to \p mask. This allows OS X
+ // servers to use the option key both as AltGr and as a modifier. If
+ // option is acting as AltGr (i.e. it generates a glyph and there are
+ // no command modifiers active) then we don't send the super modifier
+ // to clients because they'd try to match it as a command modifier.
+ void adjustAltGrModifier(const KeyIDs& ids,
+ KeyModifierMask* mask, bool isCommand) const;
+
+ // Maps an OS X virtual key id to a KeyButton. This simply remaps
+ // the ids so we don't use KeyButton 0.
+ static KeyButton mapVirtualKeyToKeyButton(UInt32 keyCode);
+
+ // Maps a KeyButton to an OS X key code. This is the inverse of
+ // mapVirtualKeyToKeyButton.
+ static UInt32 mapKeyButtonToVirtualKey(KeyButton keyButton);
+
+ void init();
+
+ // Post a key event to HID manager. It posts an event to HID client, a
+ // much lower level than window manager which's the target from carbon
+ // CGEventPost
+ void postHIDVirtualKey(const UInt8 virtualKeyCode,
+ const bool postDown);
+
+private:
+ // OS X uses a physical key if 0 for the 'A' key. barrier reserves
+ // KeyButton 0 so we offset all OS X physical key ids by this much
+ // when used as a KeyButton and by minus this much to map a KeyButton
+ // to a physical button.
+ enum {
+ KeyButtonOffset = 1
+ };
+
+ typedef std::map<CFDataRef, SInt32> GroupMap;
+ typedef std::map<UInt32, KeyID> VirtualKeyMap;
+
+ VirtualKeyMap m_virtualKeyMap;
+ mutable UInt32 m_deadKeyState;
+ GroupList m_groups;
+ GroupMap m_groupMap;
+ bool m_shiftPressed;
+ bool m_controlPressed;
+ bool m_altPressed;
+ bool m_superPressed;
+ bool m_capsPressed;
+};
diff --git a/src/lib/platform/OSXMediaKeySimulator.h b/src/lib/platform/OSXMediaKeySimulator.h
new file mode 100644
index 0000000..39739d2
--- /dev/null
+++ b/src/lib/platform/OSXMediaKeySimulator.h
@@ -0,0 +1,30 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2016 Symless.
+ *
+ * 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 COPYING 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
+
+#import <CoreFoundation/CoreFoundation.h>
+
+#include "barrier/key_types.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+bool fakeNativeMediaKey(KeyID id);
+#if defined(__cplusplus)
+}
+#endif
diff --git a/src/lib/platform/OSXMediaKeySimulator.m b/src/lib/platform/OSXMediaKeySimulator.m
new file mode 100644
index 0000000..5aacd10
--- /dev/null
+++ b/src/lib/platform/OSXMediaKeySimulator.m
@@ -0,0 +1,92 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2016 Symless.
+ *
+ * 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 COPYING 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.
+ */
+
+#import "platform/OSXMediaKeySimulator.h"
+
+#import <Cocoa/Cocoa.h>
+
+int convertKeyIDToNXKeyType(KeyID id)
+{
+ // hidsystem/ev_keymap.h
+ // NX_KEYTYPE_SOUND_UP 0
+ // NX_KEYTYPE_SOUND_DOWN 1
+ // NX_KEYTYPE_BRIGHTNESS_UP 2
+ // NX_KEYTYPE_BRIGHTNESS_DOWN 3
+ // NX_KEYTYPE_MUTE 7
+ // NX_KEYTYPE_EJECT 14
+ // NX_KEYTYPE_PLAY 16
+ // NX_KEYTYPE_NEXT 17
+ // NX_KEYTYPE_PREVIOUS 18
+ // NX_KEYTYPE_FAST 19
+ // NX_KEYTYPE_REWIND 20
+
+ int type = -1;
+ switch (id) {
+ case kKeyAudioUp:
+ type = 0;
+ break;
+ case kKeyAudioDown:
+ type = 1;
+ break;
+ case kKeyBrightnessUp:
+ type = 2;
+ break;
+ case kKeyBrightnessDown:
+ type = 3;
+ break;
+ case kKeyAudioMute:
+ type = 7;
+ break;
+ case kKeyEject:
+ type = 14;
+ break;
+ case kKeyAudioPlay:
+ type = 16;
+ break;
+ case kKeyAudioNext:
+ type = 17;
+ break;
+ case kKeyAudioPrev:
+ type = 18;
+ break;
+ default:
+ break;
+ }
+
+ return type;
+}
+
+bool
+fakeNativeMediaKey(KeyID id)
+{
+
+ NSEvent* downRef = [NSEvent otherEventWithType:NSSystemDefined
+ location: NSMakePoint(0, 0) modifierFlags:0xa00
+ timestamp:0 windowNumber:0 context:0 subtype:8
+ data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xa) << 8)
+ data2:-1];
+ CGEventRef downEvent = [downRef CGEvent];
+
+ NSEvent* upRef = [NSEvent otherEventWithType:NSSystemDefined
+ location: NSMakePoint(0, 0) modifierFlags:0xa00
+ timestamp:0 windowNumber:0 context:0 subtype:8
+ data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xb) << 8)
+ data2:-1];
+ CGEventRef upEvent = [upRef CGEvent];
+
+ CGEventPost(0, downEvent);
+ CGEventPost(0, upEvent);
+
+ return true;
+}
diff --git a/src/lib/platform/OSXMediaKeySupport.h b/src/lib/platform/OSXMediaKeySupport.h
new file mode 100644
index 0000000..d64e26e
--- /dev/null
+++ b/src/lib/platform/OSXMediaKeySupport.h
@@ -0,0 +1,33 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2016 Symless.
+ *
+ * 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 COPYING 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
+
+#import <CoreFoundation/CoreFoundation.h>
+#import <Carbon/Carbon.h>
+
+#include "barrier/key_types.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+bool fakeNativeMediaKey(KeyID id);
+bool isMediaKeyEvent(CGEventRef event);
+bool getMediaKeyEventInfo(CGEventRef event, KeyID* keyId, bool* down, bool* isRepeat);
+#if defined(__cplusplus)
+}
+#endif
diff --git a/src/lib/platform/OSXMediaKeySupport.m b/src/lib/platform/OSXMediaKeySupport.m
new file mode 100644
index 0000000..9c9dbc3
--- /dev/null
+++ b/src/lib/platform/OSXMediaKeySupport.m
@@ -0,0 +1,154 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2016 Symless.
+ *
+ * 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 COPYING 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.
+ */
+
+#import "platform/OSXMediaKeySupport.h"
+#import <Cocoa/Cocoa.h>
+#import <IOKit/hidsystem/ev_keymap.h>
+
+int convertKeyIDToNXKeyType(KeyID id)
+{
+ int type = -1;
+
+ switch (id) {
+ case kKeyAudioUp:
+ type = NX_KEYTYPE_SOUND_UP;
+ break;
+ case kKeyAudioDown:
+ type = NX_KEYTYPE_SOUND_DOWN;
+ break;
+ case kKeyBrightnessUp:
+ type = NX_KEYTYPE_BRIGHTNESS_UP;
+ break;
+ case kKeyBrightnessDown:
+ type = NX_KEYTYPE_BRIGHTNESS_DOWN;
+ break;
+ case kKeyAudioMute:
+ type = NX_KEYTYPE_MUTE;
+ break;
+ case kKeyEject:
+ type = NX_KEYTYPE_EJECT;
+ break;
+ case kKeyAudioPlay:
+ type = NX_KEYTYPE_PLAY;
+ break;
+ case kKeyAudioNext:
+ type = NX_KEYTYPE_NEXT;
+ break;
+ case kKeyAudioPrev:
+ type = NX_KEYTYPE_PREVIOUS;
+ break;
+ default:
+ break;
+ }
+
+ return type;
+}
+
+static KeyID
+convertNXKeyTypeToKeyID(uint32_t const type)
+{
+ KeyID id = 0;
+
+ switch (type) {
+ case NX_KEYTYPE_SOUND_UP:
+ id = kKeyAudioUp;
+ break;
+ case NX_KEYTYPE_SOUND_DOWN:
+ id = kKeyAudioDown;
+ break;
+ case NX_KEYTYPE_MUTE:
+ id = kKeyAudioMute;
+ break;
+ case NX_KEYTYPE_EJECT:
+ id = kKeyEject;
+ break;
+ case NX_KEYTYPE_PLAY:
+ id = kKeyAudioPlay;
+ break;
+ case NX_KEYTYPE_FAST:
+ case NX_KEYTYPE_NEXT:
+ id = kKeyAudioNext;
+ break;
+ case NX_KEYTYPE_REWIND:
+ case NX_KEYTYPE_PREVIOUS:
+ id = kKeyAudioPrev;
+ break;
+ default:
+ break;
+ }
+
+ return id;
+}
+
+bool
+isMediaKeyEvent(CGEventRef event) {
+ NSEvent* nsEvent = nil;
+ @try {
+ nsEvent = [NSEvent eventWithCGEvent: event];
+ if ([nsEvent subtype] != 8) {
+ return false;
+ }
+ uint32_t const nxKeyId = ([nsEvent data1] & 0xFFFF0000) >> 16;
+ if (convertNXKeyTypeToKeyID (nxKeyId)) {
+ return true;
+ }
+ } @catch (NSException* e) {
+ }
+ return false;
+}
+
+bool
+getMediaKeyEventInfo(CGEventRef event, KeyID* const keyId,
+ bool* const down, bool* const isRepeat) {
+ NSEvent* nsEvent = nil;
+ @try {
+ nsEvent = [NSEvent eventWithCGEvent: event];
+ } @catch (NSException* e) {
+ return false;
+ }
+ if (keyId) {
+ *keyId = convertNXKeyTypeToKeyID (([nsEvent data1] & 0xFFFF0000) >> 16);
+ }
+ if (down) {
+ *down = !([nsEvent data1] & 0x100);
+ }
+ if (isRepeat) {
+ *isRepeat = [nsEvent data1] & 0x1;
+ }
+ return true;
+}
+
+bool
+fakeNativeMediaKey(KeyID id)
+{
+
+ NSEvent* downRef = [NSEvent otherEventWithType:NSSystemDefined
+ location: NSMakePoint(0, 0) modifierFlags:0xa00
+ timestamp:0 windowNumber:0 context:0 subtype:8
+ data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xa) << 8)
+ data2:-1];
+ CGEventRef downEvent = [downRef CGEvent];
+
+ NSEvent* upRef = [NSEvent otherEventWithType:NSSystemDefined
+ location: NSMakePoint(0, 0) modifierFlags:0xa00
+ timestamp:0 windowNumber:0 context:0 subtype:8
+ data1:(convertKeyIDToNXKeyType(id) << 16) | ((0xb) << 8)
+ data2:-1];
+ CGEventRef upEvent = [upRef CGEvent];
+
+ CGEventPost(0, downEvent);
+ CGEventPost(0, upEvent);
+
+ return true;
+}
diff --git a/src/lib/platform/OSXPasteboardPeeker.h b/src/lib/platform/OSXPasteboardPeeker.h
new file mode 100644
index 0000000..5105262
--- /dev/null
+++ b/src/lib/platform/OSXPasteboardPeeker.h
@@ -0,0 +1,32 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#include "common/common.h"
+
+#import <CoreFoundation/CoreFoundation.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+CFStringRef getDraggedFileURL();
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/src/lib/platform/OSXPasteboardPeeker.m b/src/lib/platform/OSXPasteboardPeeker.m
new file mode 100644
index 0000000..ab39e26
--- /dev/null
+++ b/src/lib/platform/OSXPasteboardPeeker.m
@@ -0,0 +1,37 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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.
+ */
+
+#import "platform/OSXPasteboardPeeker.h"
+
+#import <Foundation/Foundation.h>
+#import <CoreData/CoreData.h>
+#import <Cocoa/Cocoa.h>
+
+CFStringRef
+getDraggedFileURL()
+{
+ NSString* pbName = NSDragPboard;
+ NSPasteboard* pboard = [NSPasteboard pasteboardWithName:pbName];
+
+ NSMutableString* string;
+ string = [[NSMutableString alloc] initWithCapacity:0];
+
+ NSArray* files = [pboard propertyListForType:NSFilenamesPboardType];
+ for (id file in files) {
+ [string appendString: (NSString*)file];
+ [string appendString: @"\0"];
+ }
+
+ return (CFStringRef)string;
+}
diff --git a/src/lib/platform/OSXScreen.h b/src/lib/platform/OSXScreen.h
new file mode 100644
index 0000000..27cb7df
--- /dev/null
+++ b/src/lib/platform/OSXScreen.h
@@ -0,0 +1,349 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXClipboard.h"
+#include "barrier/PlatformScreen.h"
+#include "barrier/DragInformation.h"
+#include "base/EventTypes.h"
+#include "common/stdmap.h"
+#include "common/stdvector.h"
+
+#include <bitset>
+#include <Carbon/Carbon.h>
+#include <mach/mach_port.h>
+#include <mach/mach_interface.h>
+#include <mach/mach_init.h>
+#include <IOKit/pwr_mgt/IOPMLib.h>
+#include <IOKit/IOMessage.h>
+
+extern "C" {
+ typedef int CGSConnectionID;
+ CGError CGSSetConnectionProperty(CGSConnectionID cid, CGSConnectionID targetCID, CFStringRef key, CFTypeRef value);
+ int _CGSDefaultConnection();
+}
+
+
+template <class T>
+class CondVar;
+class EventQueueTimer;
+class Mutex;
+class Thread;
+class OSXKeyState;
+class OSXScreenSaver;
+class IEventQueue;
+class Mutex;
+
+//! Implementation of IPlatformScreen for OS X
+class OSXScreen : public PlatformScreen {
+public:
+ OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCursor=true);
+ virtual ~OSXScreen();
+
+ IEventQueue* getEvents() const { return m_events; }
+
+ // IScreen overrides
+ virtual void* getEventTarget() const;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const;
+
+ // IPrimaryScreen overrides
+ virtual void reconfigure(UInt32 activeSides);
+ virtual void warpCursor(SInt32 x, SInt32 y);
+ virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask);
+ virtual void unregisterHotKey(UInt32 id);
+ virtual void fakeInputBegin();
+ virtual void fakeInputEnd();
+ virtual SInt32 getJumpZoneSize() const;
+ virtual bool isAnyMouseButtonDown(UInt32& buttonID) const;
+ virtual void getCursorCenter(SInt32& x, SInt32& y) const;
+
+ // ISecondaryScreen overrides
+ virtual void fakeMouseButton(ButtonID id, bool press);
+ virtual void fakeMouseMove(SInt32 x, SInt32 y);
+ virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const;
+ virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
+
+ // IPlatformScreen overrides
+ virtual void enable();
+ virtual void disable();
+ virtual void enter();
+ virtual bool leave();
+ virtual bool setClipboard(ClipboardID, const IClipboard*);
+ virtual void checkClipboards();
+ virtual void openScreensaver(bool notify);
+ virtual void closeScreensaver();
+ virtual void screensaver(bool activate);
+ virtual void resetOptions();
+ virtual void setOptions(const OptionsList& options);
+ virtual void setSequenceNumber(UInt32);
+ virtual bool isPrimary() const;
+ virtual void fakeDraggingFiles(DragFileList fileList);
+ virtual String& getDraggingFilename();
+
+ const String& getDropTarget() const { return m_dropTarget; }
+ void waitForCarbonLoop() const;
+
+protected:
+ // IPlatformScreen overrides
+ virtual void handleSystemEvent(const Event&, void*);
+ virtual void updateButtons();
+ virtual IKeyState* getKeyState() const;
+
+private:
+ void updateScreenShape();
+ void updateScreenShape(const CGDirectDisplayID, const CGDisplayChangeSummaryFlags);
+ void postMouseEvent(CGPoint&) const;
+
+ // convenience function to send events
+ void sendEvent(Event::Type type, void* = NULL) const;
+ void sendClipboardEvent(Event::Type type, ClipboardID id) const;
+
+ // message handlers
+ bool onMouseMove(SInt32 mx, SInt32 my);
+ // mouse button handler. pressed is true if this is a mousedown
+ // event, false if it is a mouseup event. macButton is the index
+ // of the button pressed using the mac button mapping.
+ bool onMouseButton(bool pressed, UInt16 macButton);
+ bool onMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
+
+ void constructMouseButtonEventMap();
+
+ bool onKey(CGEventRef event);
+
+ void onMediaKey(CGEventRef event);
+
+ bool onHotKey(EventRef event) const;
+
+ // Added here to allow the carbon cursor hack to be called.
+ void showCursor();
+ void hideCursor();
+
+ // map barrier mouse button to mac buttons
+ ButtonID mapBarrierButtonToMac(UInt16) const;
+
+ // map mac mouse button to barrier buttons
+ ButtonID mapMacButtonToBarrier(UInt16) const;
+
+ // map mac scroll wheel value to a barrier scroll wheel value
+ SInt32 mapScrollWheelToBarrier(SInt32) const;
+
+ // map barrier scroll wheel value to a mac scroll wheel value
+ SInt32 mapScrollWheelFromBarrier(SInt32) const;
+
+ // get the current scroll wheel speed
+ double getScrollSpeed() const;
+
+ // get the current scroll wheel speed
+ double getScrollSpeedFactor() const;
+
+ // enable/disable drag handling for buttons 3 and up
+ void enableDragTimer(bool enable);
+
+ // drag timer handler
+ void handleDrag(const Event&, void*);
+
+ // clipboard check timer handler
+ void handleClipboardCheck(const Event&, void*);
+
+ // Resolution switch callback
+ static void displayReconfigurationCallback(CGDirectDisplayID,
+ CGDisplayChangeSummaryFlags, void*);
+
+ // fast user switch callback
+ static pascal OSStatus
+ userSwitchCallback(EventHandlerCallRef nextHandler,
+ EventRef theEvent, void* inUserData);
+
+ // sleep / wakeup support
+ void watchSystemPowerThread(void*);
+ static void testCanceled(CFRunLoopTimerRef timer, void*info);
+ static void powerChangeCallback(void* refcon, io_service_t service,
+ natural_t messageType, void* messageArgument);
+ void handlePowerChangeRequest(natural_t messageType,
+ void* messageArgument);
+
+ void handleConfirmSleep(const Event& event, void*);
+
+ // global hotkey operating mode
+ static bool isGlobalHotKeyOperatingModeAvailable();
+ static void setGlobalHotKeysEnabled(bool enabled);
+ static bool getGlobalHotKeysEnabled();
+
+ // Quartz event tap support
+ static CGEventRef handleCGInputEvent(CGEventTapProxy proxy,
+ CGEventType type,
+ CGEventRef event,
+ void* refcon);
+ static CGEventRef handleCGInputEventSecondary(CGEventTapProxy proxy,
+ CGEventType type,
+ CGEventRef event,
+ void* refcon);
+
+ // convert CFString to char*
+ static char* CFStringRefToUTF8String(CFStringRef aString);
+
+ void getDropTargetThread(void*);
+
+private:
+ struct HotKeyItem {
+ public:
+ HotKeyItem(UInt32, UInt32);
+ HotKeyItem(EventHotKeyRef, UInt32, UInt32);
+
+ EventHotKeyRef getRef() const;
+
+ bool operator<(const HotKeyItem&) const;
+
+ private:
+ EventHotKeyRef m_ref;
+ UInt32 m_keycode;
+ UInt32 m_mask;
+ };
+
+ enum EMouseButtonState {
+ kMouseButtonUp = 0,
+ kMouseButtonDragged,
+ kMouseButtonDown,
+ kMouseButtonStateMax
+ };
+
+
+ class MouseButtonState {
+ public:
+ void set(UInt32 button, EMouseButtonState state);
+ bool any();
+ void reset();
+ void overwrite(UInt32 buttons);
+
+ bool test(UInt32 button) const;
+ SInt8 getFirstButtonDown() const;
+ private:
+ std::bitset<NumButtonIDs> m_buttons;
+ };
+
+ typedef std::map<UInt32, HotKeyItem> HotKeyMap;
+ typedef std::vector<UInt32> HotKeyIDList;
+ typedef std::map<KeyModifierMask, UInt32> ModifierHotKeyMap;
+ typedef std::map<HotKeyItem, UInt32> HotKeyToIDMap;
+
+ // true if screen is being used as a primary screen, false otherwise
+ bool m_isPrimary;
+
+ // true if mouse has entered the screen
+ bool m_isOnScreen;
+
+ // the display
+ CGDirectDisplayID m_displayID;
+
+ // screen shape stuff
+ SInt32 m_x, m_y;
+ SInt32 m_w, m_h;
+ SInt32 m_xCenter, m_yCenter;
+
+ // mouse state
+ mutable SInt32 m_xCursor, m_yCursor;
+ mutable bool m_cursorPosValid;
+
+ /* FIXME: this data structure is explicitly marked mutable due
+ to a need to track the state of buttons since the remote
+ side only lets us know of change events, and because the
+ fakeMouseButton button method is marked 'const'. This is
+ Evil, and this should be moved to a place where it need not
+ be mutable as soon as possible. */
+ mutable MouseButtonState m_buttonState;
+ typedef std::map<UInt16, CGEventType> MouseButtonEventMapType;
+ std::vector<MouseButtonEventMapType> MouseButtonEventMap;
+
+ bool m_cursorHidden;
+ SInt32 m_dragNumButtonsDown;
+ Point m_dragLastPoint;
+ EventQueueTimer* m_dragTimer;
+
+ // keyboard stuff
+ OSXKeyState* m_keyState;
+
+ // clipboards
+ OSXClipboard m_pasteboard;
+ UInt32 m_sequenceNumber;
+
+ // screen saver stuff
+ OSXScreenSaver* m_screensaver;
+ bool m_screensaverNotify;
+
+ // clipboard stuff
+ bool m_ownClipboard;
+ EventQueueTimer* m_clipboardTimer;
+
+ // window object that gets user input events when the server
+ // has focus.
+ WindowRef m_hiddenWindow;
+ // window object that gets user input events when the server
+ // does not have focus.
+ WindowRef m_userInputWindow;
+
+ // fast user switching
+ EventHandlerRef m_switchEventHandlerRef;
+
+ // sleep / wakeup
+ Mutex* m_pmMutex;
+ Thread* m_pmWatchThread;
+ CondVar<bool>* m_pmThreadReady;
+ CFRunLoopRef m_pmRunloop;
+ io_connect_t m_pmRootPort;
+
+ // hot key stuff
+ HotKeyMap m_hotKeys;
+ HotKeyIDList m_oldHotKeyIDs;
+ ModifierHotKeyMap m_modifierHotKeys;
+ UInt32 m_activeModifierHotKey;
+ KeyModifierMask m_activeModifierHotKeyMask;
+ HotKeyToIDMap m_hotKeyToIDMap;
+
+ // global hotkey operating mode
+ static bool s_testedForGHOM;
+ static bool s_hasGHOM;
+
+ // Quartz input event support
+ CFMachPortRef m_eventTapPort;
+ CFRunLoopSourceRef m_eventTapRLSR;
+
+ // for double click coalescing.
+ double m_lastClickTime;
+ int m_clickState;
+ SInt32 m_lastSingleClickXCursor;
+ SInt32 m_lastSingleClickYCursor;
+
+ // cursor will hide and show on enable and disable if true.
+ bool m_autoShowHideCursor;
+
+ IEventQueue* m_events;
+
+ Thread* m_getDropTargetThread;
+ String m_dropTarget;
+
+#if defined(MAC_OS_X_VERSION_10_7)
+ Mutex* m_carbonLoopMutex;
+ CondVar<bool>* m_carbonLoopReady;
+#endif
+
+ class OSXScreenImpl* m_impl;
+};
diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm
new file mode 100644
index 0000000..1d80521
--- /dev/null
+++ b/src/lib/platform/OSXScreen.mm
@@ -0,0 +1,2162 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/OSXScreen.h"
+
+#include "base/EventQueue.h"
+#include "client/Client.h"
+#include "platform/OSXClipboard.h"
+#include "platform/OSXEventQueueBuffer.h"
+#include "platform/OSXKeyState.h"
+#include "platform/OSXScreenSaver.h"
+#include "platform/OSXDragSimulator.h"
+#include "platform/OSXMediaKeySupport.h"
+#include "platform/OSXPasteboardPeeker.h"
+#include "barrier/Clipboard.h"
+#include "barrier/KeyMap.h"
+#include "barrier/ClientApp.h"
+#include "mt/CondVar.h"
+#include "mt/Lock.h"
+#include "mt/Mutex.h"
+#include "mt/Thread.h"
+#include "arch/XArch.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+#include "base/TMethodJob.h"
+
+#include <math.h>
+#include <mach-o/dyld.h>
+#include <AvailabilityMacros.h>
+#include <IOKit/hidsystem/event_status_driver.h>
+#include <AppKit/NSEvent.h>
+
+// This isn't in any Apple SDK that I know of as of yet.
+enum {
+ kBarrierEventMouseScroll = 11,
+ kBarrierMouseScrollAxisX = 'saxx',
+ kBarrierMouseScrollAxisY = 'saxy'
+};
+
+enum {
+ kCarbonLoopWaitTimeout = 10
+};
+
+// TODO: upgrade deprecated function usage in these functions.
+void setZeroSuppressionInterval();
+void avoidSupression();
+void logCursorVisibility();
+void avoidHesitatingCursor();
+
+//
+// OSXScreen
+//
+
+bool OSXScreen::s_testedForGHOM = false;
+bool OSXScreen::s_hasGHOM = false;
+
+OSXScreen::OSXScreen(IEventQueue* events, bool isPrimary, bool autoShowHideCursor) :
+ PlatformScreen(events),
+ m_isPrimary(isPrimary),
+ m_isOnScreen(m_isPrimary),
+ m_cursorPosValid(false),
+ MouseButtonEventMap(NumButtonIDs),
+ m_cursorHidden(false),
+ m_dragNumButtonsDown(0),
+ m_dragTimer(NULL),
+ m_keyState(NULL),
+ m_sequenceNumber(0),
+ m_screensaver(NULL),
+ m_screensaverNotify(false),
+ m_ownClipboard(false),
+ m_clipboardTimer(NULL),
+ m_hiddenWindow(NULL),
+ m_userInputWindow(NULL),
+ m_switchEventHandlerRef(0),
+ m_pmMutex(new Mutex),
+ m_pmWatchThread(NULL),
+ m_pmThreadReady(new CondVar<bool>(m_pmMutex, false)),
+ m_pmRootPort(0),
+ m_activeModifierHotKey(0),
+ m_activeModifierHotKeyMask(0),
+ m_eventTapPort(nullptr),
+ m_eventTapRLSR(nullptr),
+ m_lastClickTime(0),
+ m_clickState(1),
+ m_lastSingleClickXCursor(0),
+ m_lastSingleClickYCursor(0),
+ m_autoShowHideCursor(autoShowHideCursor),
+ m_events(events),
+ m_getDropTargetThread(NULL),
+ m_impl(NULL)
+{
+ try {
+ m_displayID = CGMainDisplayID();
+ updateScreenShape(m_displayID, 0);
+ m_screensaver = new OSXScreenSaver(m_events, getEventTarget());
+ m_keyState = new OSXKeyState(m_events);
+
+ // only needed when running as a server.
+ if (m_isPrimary) {
+
+#if defined(MAC_OS_X_VERSION_10_9)
+ // we can't pass options to show the dialog, this must be done by the gui.
+ if (!AXIsProcessTrusted()) {
+ throw XArch("assistive devices does not trust this process, allow it in system settings.");
+ }
+#else
+ // now deprecated in mavericks.
+ if (!AXAPIEnabled()) {
+ throw XArch("assistive devices is not enabled, enable it in system settings.");
+ }
+#endif
+ }
+
+ // install display manager notification handler
+ CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallback, this);
+
+ // install fast user switching event handler
+ EventTypeSpec switchEventTypes[2];
+ switchEventTypes[0].eventClass = kEventClassSystem;
+ switchEventTypes[0].eventKind = kEventSystemUserSessionDeactivated;
+ switchEventTypes[1].eventClass = kEventClassSystem;
+ switchEventTypes[1].eventKind = kEventSystemUserSessionActivated;
+ EventHandlerUPP switchEventHandler =
+ NewEventHandlerUPP(userSwitchCallback);
+ InstallApplicationEventHandler(switchEventHandler, 2, switchEventTypes,
+ this, &m_switchEventHandlerRef);
+ DisposeEventHandlerUPP(switchEventHandler);
+
+ constructMouseButtonEventMap();
+
+ // watch for requests to sleep
+ m_events->adoptHandler(m_events->forOSXScreen().confirmSleep(),
+ getEventTarget(),
+ new TMethodEventJob<OSXScreen>(this,
+ &OSXScreen::handleConfirmSleep));
+
+ // create thread for monitoring system power state.
+ *m_pmThreadReady = false;
+#if defined(MAC_OS_X_VERSION_10_7)
+ m_carbonLoopMutex = new Mutex();
+ m_carbonLoopReady = new CondVar<bool>(m_carbonLoopMutex, false);
+#endif
+ LOG((CLOG_DEBUG "starting watchSystemPowerThread"));
+ m_pmWatchThread = new Thread(new TMethodJob<OSXScreen>
+ (this, &OSXScreen::watchSystemPowerThread));
+ }
+ catch (...) {
+ m_events->removeHandler(m_events->forOSXScreen().confirmSleep(),
+ getEventTarget());
+ if (m_switchEventHandlerRef != 0) {
+ RemoveEventHandler(m_switchEventHandlerRef);
+ }
+
+ CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
+
+ delete m_keyState;
+ delete m_screensaver;
+ throw;
+ }
+
+ // install event handlers
+ m_events->adoptHandler(Event::kSystem, m_events->getSystemTarget(),
+ new TMethodEventJob<OSXScreen>(this,
+ &OSXScreen::handleSystemEvent));
+
+ // install the platform event queue
+ m_events->adoptBuffer(new OSXEventQueueBuffer(m_events));
+}
+
+OSXScreen::~OSXScreen()
+{
+ disable();
+ m_events->adoptBuffer(NULL);
+ m_events->removeHandler(Event::kSystem, m_events->getSystemTarget());
+
+ if (m_pmWatchThread) {
+ // make sure the thread has setup the runloop.
+ {
+ Lock lock(m_pmMutex);
+ while (!(bool)*m_pmThreadReady) {
+ m_pmThreadReady->wait();
+ }
+ }
+
+ // now exit the thread's runloop and wait for it to exit
+ LOG((CLOG_DEBUG "stopping watchSystemPowerThread"));
+ CFRunLoopStop(m_pmRunloop);
+ m_pmWatchThread->wait();
+ delete m_pmWatchThread;
+ m_pmWatchThread = NULL;
+ }
+ delete m_pmThreadReady;
+ delete m_pmMutex;
+
+ m_events->removeHandler(m_events->forOSXScreen().confirmSleep(),
+ getEventTarget());
+
+ RemoveEventHandler(m_switchEventHandlerRef);
+
+ CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
+
+ delete m_keyState;
+ delete m_screensaver;
+
+#if defined(MAC_OS_X_VERSION_10_7)
+ delete m_carbonLoopMutex;
+ delete m_carbonLoopReady;
+#endif
+}
+
+void*
+OSXScreen::getEventTarget() const
+{
+ return const_cast<OSXScreen*>(this);
+}
+
+bool
+OSXScreen::getClipboard(ClipboardID, IClipboard* dst) const
+{
+ Clipboard::copy(dst, &m_pasteboard);
+ return true;
+}
+
+void
+OSXScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
+{
+ x = m_x;
+ y = m_y;
+ w = m_w;
+ h = m_h;
+}
+
+void
+OSXScreen::getCursorPos(SInt32& x, SInt32& y) const
+{
+ CGEventRef event = CGEventCreate(NULL);
+ CGPoint mouse = CGEventGetLocation(event);
+ x = mouse.x;
+ y = mouse.y;
+ m_cursorPosValid = true;
+ m_xCursor = x;
+ m_yCursor = y;
+ CFRelease(event);
+}
+
+void
+OSXScreen::reconfigure(UInt32)
+{
+ // do nothing
+}
+
+void
+OSXScreen::warpCursor(SInt32 x, SInt32 y)
+{
+ // move cursor without generating events
+ CGPoint pos;
+ pos.x = x;
+ pos.y = y;
+ CGWarpMouseCursorPosition(pos);
+
+ // save new cursor position
+ m_xCursor = x;
+ m_yCursor = y;
+ m_cursorPosValid = true;
+}
+
+void
+OSXScreen::fakeInputBegin()
+{
+ // FIXME -- not implemented
+}
+
+void
+OSXScreen::fakeInputEnd()
+{
+ // FIXME -- not implemented
+}
+
+SInt32
+OSXScreen::getJumpZoneSize() const
+{
+ return 1;
+}
+
+bool
+OSXScreen::isAnyMouseButtonDown(UInt32& buttonID) const
+{
+ if (m_buttonState.test(0)) {
+ buttonID = kButtonLeft;
+ return true;
+ }
+
+ return (GetCurrentButtonState() != 0);
+}
+
+void
+OSXScreen::getCursorCenter(SInt32& x, SInt32& y) const
+{
+ x = m_xCenter;
+ y = m_yCenter;
+}
+
+UInt32
+OSXScreen::registerHotKey(KeyID key, KeyModifierMask mask)
+{
+ // get mac virtual key and modifier mask matching barrier key and mask
+ UInt32 macKey, macMask;
+ if (!m_keyState->mapBarrierHotKeyToMac(key, mask, macKey, macMask)) {
+ LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
+ return 0;
+ }
+
+ // choose hotkey id
+ UInt32 id;
+ if (!m_oldHotKeyIDs.empty()) {
+ id = m_oldHotKeyIDs.back();
+ m_oldHotKeyIDs.pop_back();
+ }
+ else {
+ id = m_hotKeys.size() + 1;
+ }
+
+ // if this hot key has modifiers only then we'll handle it specially
+ EventHotKeyRef ref = NULL;
+ bool okay;
+ if (key == kKeyNone) {
+ if (m_modifierHotKeys.count(mask) > 0) {
+ // already registered
+ okay = false;
+ }
+ else {
+ m_modifierHotKeys[mask] = id;
+ okay = true;
+ }
+ }
+ else {
+ EventHotKeyID hkid = { 'SNRG', (UInt32)id };
+ OSStatus status = RegisterEventHotKey(macKey, macMask, hkid,
+ GetApplicationEventTarget(), 0,
+ &ref);
+ okay = (status == noErr);
+ m_hotKeyToIDMap[HotKeyItem(macKey, macMask)] = id;
+ }
+
+ if (!okay) {
+ m_oldHotKeyIDs.push_back(id);
+ m_hotKeyToIDMap.erase(HotKeyItem(macKey, macMask));
+ LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask));
+ return 0;
+ }
+
+ m_hotKeys.insert(std::make_pair(id, HotKeyItem(ref, macKey, macMask)));
+
+ LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask, id));
+ return id;
+}
+
+void
+OSXScreen::unregisterHotKey(UInt32 id)
+{
+ // look up hotkey
+ HotKeyMap::iterator i = m_hotKeys.find(id);
+ if (i == m_hotKeys.end()) {
+ return;
+ }
+
+ // unregister with OS
+ bool okay;
+ if (i->second.getRef() != NULL) {
+ okay = (UnregisterEventHotKey(i->second.getRef()) == noErr);
+ }
+ else {
+ okay = false;
+ // XXX -- this is inefficient
+ for (ModifierHotKeyMap::iterator j = m_modifierHotKeys.begin();
+ j != m_modifierHotKeys.end(); ++j) {
+ if (j->second == id) {
+ m_modifierHotKeys.erase(j);
+ okay = true;
+ break;
+ }
+ }
+ }
+ if (!okay) {
+ LOG((CLOG_WARN "failed to unregister hotkey id=%d", id));
+ }
+ else {
+ LOG((CLOG_DEBUG "unregistered hotkey id=%d", id));
+ }
+
+ // discard hot key from map and record old id for reuse
+ m_hotKeyToIDMap.erase(i->second);
+ m_hotKeys.erase(i);
+ m_oldHotKeyIDs.push_back(id);
+ if (m_activeModifierHotKey == id) {
+ m_activeModifierHotKey = 0;
+ m_activeModifierHotKeyMask = 0;
+ }
+}
+
+void
+OSXScreen::constructMouseButtonEventMap()
+{
+ const CGEventType source[NumButtonIDs][3] = {
+ {kCGEventLeftMouseUp, kCGEventLeftMouseDragged, kCGEventLeftMouseDown},
+ {kCGEventRightMouseUp, kCGEventRightMouseDragged, kCGEventRightMouseDown},
+ {kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventOtherMouseDown},
+ {kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventOtherMouseDown},
+ {kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventOtherMouseDown}
+ };
+
+ for (UInt16 button = 0; button < NumButtonIDs; button++) {
+ MouseButtonEventMapType new_map;
+ for (UInt16 state = (UInt32) kMouseButtonUp; state < kMouseButtonStateMax; state++) {
+ CGEventType curEvent = source[button][state];
+ new_map[state] = curEvent;
+ }
+ MouseButtonEventMap[button] = new_map;
+ }
+}
+
+void
+OSXScreen::postMouseEvent(CGPoint& pos) const
+{
+ // check if cursor position is valid on the client display configuration
+ // stkamp@users.sourceforge.net
+ CGDisplayCount displayCount = 0;
+ CGGetDisplaysWithPoint(pos, 0, NULL, &displayCount);
+ if (displayCount == 0) {
+ // cursor position invalid - clamp to bounds of last valid display.
+ // find the last valid display using the last cursor position.
+ displayCount = 0;
+ CGDirectDisplayID displayID;
+ CGGetDisplaysWithPoint(CGPointMake(m_xCursor, m_yCursor), 1,
+ &displayID, &displayCount);
+ if (displayCount != 0) {
+ CGRect displayRect = CGDisplayBounds(displayID);
+ if (pos.x < displayRect.origin.x) {
+ pos.x = displayRect.origin.x;
+ }
+ else if (pos.x > displayRect.origin.x +
+ displayRect.size.width - 1) {
+ pos.x = displayRect.origin.x + displayRect.size.width - 1;
+ }
+ if (pos.y < displayRect.origin.y) {
+ pos.y = displayRect.origin.y;
+ }
+ else if (pos.y > displayRect.origin.y +
+ displayRect.size.height - 1) {
+ pos.y = displayRect.origin.y + displayRect.size.height - 1;
+ }
+ }
+ }
+
+ CGEventType type = kCGEventMouseMoved;
+
+ SInt8 button = m_buttonState.getFirstButtonDown();
+ if (button != -1) {
+ MouseButtonEventMapType thisButtonType = MouseButtonEventMap[button];
+ type = thisButtonType[kMouseButtonDragged];
+ }
+
+ CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, static_cast<CGMouseButton>(button));
+
+ // Dragging events also need the click state
+ CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState);
+
+ // Fix for sticky keys
+ CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags();
+ CGEventSetFlags(event, modifiers);
+
+ // Set movement deltas to fix issues with certain 3D programs
+ SInt64 deltaX = pos.x;
+ deltaX -= m_xCursor;
+
+ SInt64 deltaY = pos.y;
+ deltaY -= m_yCursor;
+
+ CGEventSetIntegerValueField(event, kCGMouseEventDeltaX, deltaX);
+ CGEventSetIntegerValueField(event, kCGMouseEventDeltaY, deltaY);
+
+ double deltaFX = deltaX;
+ double deltaFY = deltaY;
+
+ CGEventSetDoubleValueField(event, kCGMouseEventDeltaX, deltaFX);
+ CGEventSetDoubleValueField(event, kCGMouseEventDeltaY, deltaFY);
+
+ CGEventPost(kCGHIDEventTap, event);
+
+ CFRelease(event);
+}
+
+void
+OSXScreen::fakeMouseButton(ButtonID id, bool press)
+{
+ // Buttons are indexed from one, but the button down array is indexed from zero
+ UInt32 index = mapBarrierButtonToMac(id) - kButtonLeft;
+ if (index >= NumButtonIDs) {
+ return;
+ }
+
+ CGPoint pos;
+ if (!m_cursorPosValid) {
+ SInt32 x, y;
+ getCursorPos(x, y);
+ }
+ pos.x = m_xCursor;
+ pos.y = m_yCursor;
+
+ // variable used to detect mouse coordinate differences between
+ // old & new mouse clicks. Used in double click detection.
+ SInt32 xDiff = m_xCursor - m_lastSingleClickXCursor;
+ SInt32 yDiff = m_yCursor - m_lastSingleClickYCursor;
+ double diff = sqrt(xDiff * xDiff + yDiff * yDiff);
+ // max sqrt(x^2 + y^2) difference allowed to double click
+ // since we don't have double click distance in NX APIs
+ // we define our own defaults.
+ const double maxDiff = sqrt(2) + 0.0001;
+
+ double clickTime = [NSEvent doubleClickInterval];
+
+ // As long as the click is within the time window and distance window
+ // increase clickState (double click, triple click, etc)
+ // This will allow for higher than triple click but the quartz documenation
+ // does not specify that this should be limited to triple click
+ if (press) {
+ if ((ARCH->time() - m_lastClickTime) <= clickTime && diff <= maxDiff){
+ m_clickState++;
+ }
+ else {
+ m_clickState = 1;
+ }
+
+ m_lastClickTime = ARCH->time();
+ }
+
+ if (m_clickState == 1){
+ m_lastSingleClickXCursor = m_xCursor;
+ m_lastSingleClickYCursor = m_yCursor;
+ }
+
+ EMouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp;
+
+ LOG((CLOG_DEBUG1 "faking mouse button id: %d press: %s", index, press ? "pressed" : "released"));
+
+ MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index];
+ CGEventType type = thisButtonMap[state];
+
+ CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, static_cast<CGMouseButton>(index));
+
+ CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState);
+
+ // Fix for sticky keys
+ CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags();
+ CGEventSetFlags(event, modifiers);
+
+ m_buttonState.set(index, state);
+ CGEventPost(kCGHIDEventTap, event);
+
+ CFRelease(event);
+
+ if (!press && (id == kButtonLeft)) {
+ if (m_fakeDraggingStarted) {
+ m_getDropTargetThread = new Thread(new TMethodJob<OSXScreen>(
+ this, &OSXScreen::getDropTargetThread));
+ }
+
+ m_draggingStarted = false;
+ }
+}
+
+void
+OSXScreen::getDropTargetThread(void*)
+{
+#if defined(MAC_OS_X_VERSION_10_7)
+ char* cstr = NULL;
+
+ // wait for 5 secs for the drop destinaiton string to be filled.
+ UInt32 timeout = ARCH->time() + 5;
+
+ while (ARCH->time() < timeout) {
+ CFStringRef cfstr = getCocoaDropTarget();
+ cstr = CFStringRefToUTF8String(cfstr);
+ CFRelease(cfstr);
+
+ if (cstr != NULL) {
+ break;
+ }
+ ARCH->sleep(.1f);
+ }
+
+ if (cstr != NULL) {
+ LOG((CLOG_DEBUG "drop target: %s", cstr));
+ m_dropTarget = cstr;
+ }
+ else {
+ LOG((CLOG_ERR "failed to get drop target"));
+ m_dropTarget.clear();
+ }
+#else
+ LOG((CLOG_WARN "drag drop not supported"));
+#endif
+ m_fakeDraggingStarted = false;
+}
+
+void
+OSXScreen::fakeMouseMove(SInt32 x, SInt32 y)
+{
+ if (m_fakeDraggingStarted) {
+ m_buttonState.set(0, kMouseButtonDown);
+ }
+
+ // index 0 means left mouse button
+ if (m_buttonState.test(0)) {
+ m_draggingStarted = true;
+ }
+
+ // synthesize event
+ CGPoint pos;
+ pos.x = x;
+ pos.y = y;
+ postMouseEvent(pos);
+
+ // save new cursor position
+ m_xCursor = static_cast<SInt32>(pos.x);
+ m_yCursor = static_cast<SInt32>(pos.y);
+ m_cursorPosValid = true;
+}
+
+void
+OSXScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
+{
+ // OS X does not appear to have a fake relative mouse move function.
+ // simulate it by getting the current mouse position and adding to
+ // that. this can yield the wrong answer but there's not much else
+ // we can do.
+
+ // get current position
+ CGEventRef event = CGEventCreate(NULL);
+ CGPoint oldPos = CGEventGetLocation(event);
+ CFRelease(event);
+
+ // synthesize event
+ CGPoint pos;
+ m_xCursor = static_cast<SInt32>(oldPos.x);
+ m_yCursor = static_cast<SInt32>(oldPos.y);
+ pos.x = oldPos.x + dx;
+ pos.y = oldPos.y + dy;
+ postMouseEvent(pos);
+
+ // we now assume we don't know the current cursor position
+ m_cursorPosValid = false;
+}
+
+void
+OSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
+{
+ if (xDelta != 0 || yDelta != 0) {
+ // create a scroll event, post it and release it. not sure if kCGScrollEventUnitLine
+ // is the right choice here over kCGScrollEventUnitPixel
+ CGEventRef scrollEvent = CGEventCreateScrollWheelEvent(
+ NULL, kCGScrollEventUnitLine, 2,
+ mapScrollWheelFromBarrier(yDelta),
+ -mapScrollWheelFromBarrier(xDelta));
+
+ // Fix for sticky keys
+ CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags();
+ CGEventSetFlags(scrollEvent, modifiers);
+
+ CGEventPost(kCGHIDEventTap, scrollEvent);
+ CFRelease(scrollEvent);
+ }
+}
+
+void
+OSXScreen::showCursor()
+{
+ LOG((CLOG_DEBUG "showing cursor"));
+
+ CFStringRef propertyString = CFStringCreateWithCString(
+ NULL, "SetsCursorInBackground", kCFStringEncodingMacRoman);
+
+ CGSSetConnectionProperty(
+ _CGSDefaultConnection(), _CGSDefaultConnection(),
+ propertyString, kCFBooleanTrue);
+
+ CFRelease(propertyString);
+
+ CGError error = CGDisplayShowCursor(m_displayID);
+ if (error != kCGErrorSuccess) {
+ LOG((CLOG_ERR "failed to show cursor, error=%d", error));
+ }
+
+ // appears to fix "mouse randomly not showing" bug
+ CGAssociateMouseAndMouseCursorPosition(true);
+
+ logCursorVisibility();
+
+ m_cursorHidden = false;
+}
+
+void
+OSXScreen::hideCursor()
+{
+ LOG((CLOG_DEBUG "hiding cursor"));
+
+ CFStringRef propertyString = CFStringCreateWithCString(
+ NULL, "SetsCursorInBackground", kCFStringEncodingMacRoman);
+
+ CGSSetConnectionProperty(
+ _CGSDefaultConnection(), _CGSDefaultConnection(),
+ propertyString, kCFBooleanTrue);
+
+ CFRelease(propertyString);
+
+ CGError error = CGDisplayHideCursor(m_displayID);
+ if (error != kCGErrorSuccess) {
+ LOG((CLOG_ERR "failed to hide cursor, error=%d", error));
+ }
+
+ // appears to fix "mouse randomly not hiding" bug
+ CGAssociateMouseAndMouseCursorPosition(true);
+
+ logCursorVisibility();
+
+ m_cursorHidden = true;
+}
+
+void
+OSXScreen::enable()
+{
+ // watch the clipboard
+ m_clipboardTimer = m_events->newTimer(1.0, NULL);
+ m_events->adoptHandler(Event::kTimer, m_clipboardTimer,
+ new TMethodEventJob<OSXScreen>(this,
+ &OSXScreen::handleClipboardCheck));
+
+ if (m_isPrimary) {
+ // FIXME -- start watching jump zones
+
+ // kCGEventTapOptionDefault = 0x00000000 (Missing in 10.4, so specified literally)
+ m_eventTapPort = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault,
+ kCGEventMaskForAllEvents,
+ handleCGInputEvent,
+ this);
+ }
+ else {
+ // FIXME -- prevent system from entering power save mode
+
+ if (m_autoShowHideCursor) {
+ hideCursor();
+ }
+
+ // warp the mouse to the cursor center
+ fakeMouseMove(m_xCenter, m_yCenter);
+
+ // there may be a better way to do this, but we register an event handler even if we're
+ // not on the primary display (acting as a client). This way, if a local event comes in
+ // (either keyboard or mouse), we can make sure to show the cursor if we've hidden it.
+ m_eventTapPort = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault,
+ kCGEventMaskForAllEvents,
+ handleCGInputEventSecondary,
+ this);
+ }
+
+ if (!m_eventTapPort) {
+ LOG((CLOG_ERR "failed to create quartz event tap"));
+ }
+
+ m_eventTapRLSR = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapPort, 0);
+ if (!m_eventTapRLSR) {
+ LOG((CLOG_ERR "failed to create a CFRunLoopSourceRef for the quartz event tap"));
+ }
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode);
+}
+
+void
+OSXScreen::disable()
+{
+ if (m_autoShowHideCursor) {
+ showCursor();
+ }
+
+ // FIXME -- stop watching jump zones, stop capturing input
+
+ if (m_eventTapRLSR) {
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode);
+ CFRelease(m_eventTapRLSR);
+ m_eventTapRLSR = nullptr;
+ }
+
+ if (m_eventTapPort) {
+ CGEventTapEnable(m_eventTapPort, false);
+ CFRelease(m_eventTapPort);
+ m_eventTapPort = nullptr;
+ }
+ // FIXME -- allow system to enter power saving mode
+
+ // disable drag handling
+ m_dragNumButtonsDown = 0;
+ enableDragTimer(false);
+
+ // uninstall clipboard timer
+ if (m_clipboardTimer != NULL) {
+ m_events->removeHandler(Event::kTimer, m_clipboardTimer);
+ m_events->deleteTimer(m_clipboardTimer);
+ m_clipboardTimer = NULL;
+ }
+
+ m_isOnScreen = m_isPrimary;
+}
+
+void
+OSXScreen::enter()
+{
+ showCursor();
+
+ if (m_isPrimary) {
+ setZeroSuppressionInterval();
+ }
+ else {
+ // reset buttons
+ m_buttonState.reset();
+
+ // patch by Yutaka Tsutano
+ // wakes the client screen
+ // http://symless.com/spit/issues/details/3287#c12
+ io_registry_entry_t entry = IORegistryEntryFromPath(
+ kIOMasterPortDefault,
+ "IOService:/IOResources/IODisplayWrangler");
+
+ if (entry != MACH_PORT_NULL) {
+ IORegistryEntrySetCFProperty(entry, CFSTR("IORequestIdle"), kCFBooleanFalse);
+ IOObjectRelease(entry);
+ }
+
+ avoidSupression();
+ }
+
+ // now on screen
+ m_isOnScreen = true;
+}
+
+bool
+OSXScreen::leave()
+{
+ hideCursor();
+
+ if (isDraggingStarted()) {
+ String& fileList = getDraggingFilename();
+
+ if (!m_isPrimary) {
+ if (fileList.empty() == false) {
+ ClientApp& app = ClientApp::instance();
+ Client* client = app.getClientPtr();
+
+ DragInformation di;
+ di.setFilename(fileList);
+ DragFileList dragFileList;
+ dragFileList.push_back(di);
+ String info;
+ UInt32 fileCount = DragInformation::setupDragInfo(
+ dragFileList, info);
+ client->sendDragInfo(fileCount, info, info.size());
+ LOG((CLOG_DEBUG "send dragging file to server"));
+
+ // TODO: what to do with multiple file or even
+ // a folder
+ client->sendFileToServer(fileList.c_str());
+ }
+ }
+ m_draggingStarted = false;
+ }
+
+ if (m_isPrimary) {
+ avoidHesitatingCursor();
+
+ }
+
+ // now off screen
+ m_isOnScreen = false;
+
+ return true;
+}
+
+bool
+OSXScreen::setClipboard(ClipboardID, const IClipboard* src)
+{
+ if (src != NULL) {
+ LOG((CLOG_DEBUG "setting clipboard"));
+ Clipboard::copy(&m_pasteboard, src);
+ }
+ return true;
+}
+
+void
+OSXScreen::checkClipboards()
+{
+ LOG((CLOG_DEBUG2 "checking clipboard"));
+ if (m_pasteboard.synchronize()) {
+ LOG((CLOG_DEBUG "clipboard changed"));
+ sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardClipboard);
+ sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), kClipboardSelection);
+ }
+}
+
+void
+OSXScreen::openScreensaver(bool notify)
+{
+ m_screensaverNotify = notify;
+ if (!m_screensaverNotify) {
+ m_screensaver->disable();
+ }
+}
+
+void
+OSXScreen::closeScreensaver()
+{
+ if (!m_screensaverNotify) {
+ m_screensaver->enable();
+ }
+}
+
+void
+OSXScreen::screensaver(bool activate)
+{
+ if (activate) {
+ m_screensaver->activate();
+ }
+ else {
+ m_screensaver->deactivate();
+ }
+}
+
+void
+OSXScreen::resetOptions()
+{
+ // no options
+}
+
+void
+OSXScreen::setOptions(const OptionsList&)
+{
+ // no options
+}
+
+void
+OSXScreen::setSequenceNumber(UInt32 seqNum)
+{
+ m_sequenceNumber = seqNum;
+}
+
+bool
+OSXScreen::isPrimary() const
+{
+ return m_isPrimary;
+}
+
+void
+OSXScreen::sendEvent(Event::Type type, void* data) const
+{
+ m_events->addEvent(Event(type, getEventTarget(), data));
+}
+
+void
+OSXScreen::sendClipboardEvent(Event::Type type, ClipboardID id) const
+{
+ ClipboardInfo* info = (ClipboardInfo*)malloc(sizeof(ClipboardInfo));
+ info->m_id = id;
+ info->m_sequenceNumber = m_sequenceNumber;
+ sendEvent(type, info);
+}
+
+void
+OSXScreen::handleSystemEvent(const Event& event, void*)
+{
+ EventRef* carbonEvent = static_cast<EventRef*>(event.getData());
+ assert(carbonEvent != NULL);
+
+ UInt32 eventClass = GetEventClass(*carbonEvent);
+
+ switch (eventClass) {
+ case kEventClassMouse:
+ switch (GetEventKind(*carbonEvent)) {
+ case kBarrierEventMouseScroll:
+ {
+ OSStatus r;
+ long xScroll;
+ long yScroll;
+
+ // get scroll amount
+ r = GetEventParameter(*carbonEvent,
+ kBarrierMouseScrollAxisX,
+ typeSInt32,
+ NULL,
+ sizeof(xScroll),
+ NULL,
+ &xScroll);
+ if (r != noErr) {
+ xScroll = 0;
+ }
+ r = GetEventParameter(*carbonEvent,
+ kBarrierMouseScrollAxisY,
+ typeSInt32,
+ NULL,
+ sizeof(yScroll),
+ NULL,
+ &yScroll);
+ if (r != noErr) {
+ yScroll = 0;
+ }
+
+ if (xScroll != 0 || yScroll != 0) {
+ onMouseWheel(-mapScrollWheelToBarrier(xScroll),
+ mapScrollWheelToBarrier(yScroll));
+ }
+ }
+ }
+ break;
+
+ case kEventClassKeyboard:
+ switch (GetEventKind(*carbonEvent)) {
+ case kEventHotKeyPressed:
+ case kEventHotKeyReleased:
+ onHotKey(*carbonEvent);
+ break;
+ }
+
+ break;
+
+ case kEventClassWindow:
+ // 2nd param was formerly GetWindowEventTarget(m_userInputWindow) which is 32-bit only,
+ // however as m_userInputWindow is never initialized to anything we can take advantage of
+ // the fact that GetWindowEventTarget(NULL) == NULL
+ SendEventToEventTarget(*carbonEvent, NULL);
+ switch (GetEventKind(*carbonEvent)) {
+ case kEventWindowActivated:
+ LOG((CLOG_DEBUG1 "window activated"));
+ break;
+
+ case kEventWindowDeactivated:
+ LOG((CLOG_DEBUG1 "window deactivated"));
+ break;
+
+ case kEventWindowFocusAcquired:
+ LOG((CLOG_DEBUG1 "focus acquired"));
+ break;
+
+ case kEventWindowFocusRelinquish:
+ LOG((CLOG_DEBUG1 "focus released"));
+ break;
+ }
+ break;
+
+ default:
+ SendEventToEventTarget(*carbonEvent, GetEventDispatcherTarget());
+ break;
+ }
+}
+
+bool
+OSXScreen::onMouseMove(SInt32 mx, SInt32 my)
+{
+ LOG((CLOG_DEBUG2 "mouse move %+d,%+d", mx, my));
+
+ SInt32 x = mx - m_xCursor;
+ SInt32 y = my - m_yCursor;
+
+ if ((x == 0 && y == 0) || (mx == m_xCenter && mx == m_yCenter)) {
+ return true;
+ }
+
+ // save position to compute delta of next motion
+ m_xCursor = mx;
+ m_yCursor = my;
+
+ if (m_isOnScreen) {
+ // motion on primary screen
+ sendEvent(m_events->forIPrimaryScreen().motionOnPrimary(),
+ MotionInfo::alloc(m_xCursor, m_yCursor));
+ if (m_buttonState.test(0)) {
+ m_draggingStarted = true;
+ }
+ }
+ else {
+ // motion on secondary screen. warp mouse back to
+ // center.
+ warpCursor(m_xCenter, m_yCenter);
+
+ // examine the motion. if it's about the distance
+ // from the center of the screen to an edge then
+ // it's probably a bogus motion that we want to
+ // ignore (see warpCursorNoFlush() for a further
+ // description).
+ static SInt32 bogusZoneSize = 10;
+ if (-x + bogusZoneSize > m_xCenter - m_x ||
+ x + bogusZoneSize > m_x + m_w - m_xCenter ||
+ -y + bogusZoneSize > m_yCenter - m_y ||
+ y + bogusZoneSize > m_y + m_h - m_yCenter) {
+ LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y));
+ }
+ else {
+ // send motion
+ sendEvent(m_events->forIPrimaryScreen().motionOnSecondary(), MotionInfo::alloc(x, y));
+ }
+ }
+
+ return true;
+}
+
+bool
+OSXScreen::onMouseButton(bool pressed, UInt16 macButton)
+{
+ // Buttons 2 and 3 are inverted on the mac
+ ButtonID button = mapMacButtonToBarrier(macButton);
+
+ if (pressed) {
+ LOG((CLOG_DEBUG1 "event: button press button=%d", button));
+ if (button != kButtonNone) {
+ KeyModifierMask mask = m_keyState->getActiveModifiers();
+ sendEvent(m_events->forIPrimaryScreen().buttonDown(), ButtonInfo::alloc(button, mask));
+ }
+ }
+ else {
+ LOG((CLOG_DEBUG1 "event: button release button=%d", button));
+ if (button != kButtonNone) {
+ KeyModifierMask mask = m_keyState->getActiveModifiers();
+ sendEvent(m_events->forIPrimaryScreen().buttonUp(), ButtonInfo::alloc(button, mask));
+ }
+ }
+
+ // handle drags with any button other than button 1 or 2
+ if (macButton > 2) {
+ if (pressed) {
+ // one more button
+ if (m_dragNumButtonsDown++ == 0) {
+ enableDragTimer(true);
+ }
+ }
+ else {
+ // one less button
+ if (--m_dragNumButtonsDown == 0) {
+ enableDragTimer(false);
+ }
+ }
+ }
+
+ if (macButton == kButtonLeft) {
+ EMouseButtonState state = pressed ? kMouseButtonDown : kMouseButtonUp;
+ m_buttonState.set(kButtonLeft - 1, state);
+ if (pressed) {
+ m_draggingFilename.clear();
+ LOG((CLOG_DEBUG2 "dragging file directory is cleared"));
+ }
+ else {
+ if (m_fakeDraggingStarted) {
+ m_getDropTargetThread = new Thread(new TMethodJob<OSXScreen>(
+ this, &OSXScreen::getDropTargetThread));
+ }
+
+ m_draggingStarted = false;
+ }
+ }
+
+ return true;
+}
+
+bool
+OSXScreen::onMouseWheel(SInt32 xDelta, SInt32 yDelta) const
+{
+ LOG((CLOG_DEBUG1 "event: button wheel delta=%+d,%+d", xDelta, yDelta));
+ sendEvent(m_events->forIPrimaryScreen().wheel(), WheelInfo::alloc(xDelta, yDelta));
+ return true;
+}
+
+void
+OSXScreen::handleClipboardCheck(const Event&, void*)
+{
+ checkClipboards();
+}
+
+void
+OSXScreen::displayReconfigurationCallback(CGDirectDisplayID displayID, CGDisplayChangeSummaryFlags flags, void* inUserData)
+{
+ OSXScreen* screen = (OSXScreen*)inUserData;
+
+ // Closing or opening the lid when an external monitor is
+ // connected causes an kCGDisplayBeginConfigurationFlag event
+ CGDisplayChangeSummaryFlags mask = kCGDisplayBeginConfigurationFlag | kCGDisplayMovedFlag |
+ kCGDisplaySetModeFlag | kCGDisplayAddFlag | kCGDisplayRemoveFlag |
+ kCGDisplayEnabledFlag | kCGDisplayDisabledFlag |
+ kCGDisplayMirrorFlag | kCGDisplayUnMirrorFlag |
+ kCGDisplayDesktopShapeChangedFlag;
+
+ LOG((CLOG_DEBUG1 "event: display was reconfigured: %x %x %x", flags, mask, flags & mask));
+
+ if (flags & mask) { /* Something actually did change */
+
+ LOG((CLOG_DEBUG1 "event: screen changed shape; refreshing dimensions"));
+ screen->updateScreenShape(displayID, flags);
+ }
+}
+
+bool
+OSXScreen::onKey(CGEventRef event)
+{
+ CGEventType eventKind = CGEventGetType(event);
+
+ // get the key and active modifiers
+ UInt32 virtualKey = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
+ CGEventFlags macMask = CGEventGetFlags(event);
+ LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey));
+
+ // Special handling to track state of modifiers
+ if (eventKind == kCGEventFlagsChanged) {
+ // get old and new modifier state
+ KeyModifierMask oldMask = getActiveModifiers();
+ KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask);
+ m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask);
+
+ // if the current set of modifiers exactly matches a modifiers-only
+ // hot key then generate a hot key down event.
+ if (m_activeModifierHotKey == 0) {
+ if (m_modifierHotKeys.count(newMask) > 0) {
+ m_activeModifierHotKey = m_modifierHotKeys[newMask];
+ m_activeModifierHotKeyMask = newMask;
+ m_events->addEvent(Event(m_events->forIPrimaryScreen().hotKeyDown(),
+ getEventTarget(),
+ HotKeyInfo::alloc(m_activeModifierHotKey)));
+ }
+ }
+
+ // if a modifiers-only hot key is active and should no longer be
+ // then generate a hot key up event.
+ else if (m_activeModifierHotKey != 0) {
+ KeyModifierMask mask = (newMask & m_activeModifierHotKeyMask);
+ if (mask != m_activeModifierHotKeyMask) {
+ m_events->addEvent(Event(m_events->forIPrimaryScreen().hotKeyUp(),
+ getEventTarget(),
+ HotKeyInfo::alloc(m_activeModifierHotKey)));
+ m_activeModifierHotKey = 0;
+ m_activeModifierHotKeyMask = 0;
+ }
+ }
+
+ return true;
+ }
+
+ // check for hot key. when we're on a secondary screen we disable
+ // all hotkeys so we can capture the OS defined hot keys as regular
+ // keystrokes but that means we don't get our own hot keys either.
+ // so we check for a key/modifier match in our hot key map.
+ if (!m_isOnScreen) {
+ HotKeyToIDMap::const_iterator i =
+ m_hotKeyToIDMap.find(HotKeyItem(virtualKey,
+ m_keyState->mapModifiersToCarbon(macMask)
+ & 0xff00u));
+ if (i != m_hotKeyToIDMap.end()) {
+ UInt32 id = i->second;
+
+ // determine event type
+ Event::Type type;
+ //UInt32 eventKind = GetEventKind(event);
+ if (eventKind == kCGEventKeyDown) {
+ type = m_events->forIPrimaryScreen().hotKeyDown();
+ }
+ else if (eventKind == kCGEventKeyUp) {
+ type = m_events->forIPrimaryScreen().hotKeyUp();
+ }
+ else {
+ return false;
+ }
+
+ m_events->addEvent(Event(type, getEventTarget(),
+ HotKeyInfo::alloc(id)));
+
+ return true;
+ }
+ }
+
+ // decode event type
+ bool down = (eventKind == kCGEventKeyDown);
+ bool up = (eventKind == kCGEventKeyUp);
+ bool isRepeat = (CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat) == 1);
+
+ // map event to keys
+ KeyModifierMask mask;
+ OSXKeyState::KeyIDs keys;
+ KeyButton button = m_keyState->mapKeyFromEvent(keys, &mask, event);
+ if (button == 0) {
+ return false;
+ }
+
+ // check for AltGr in mask. if set we send neither the AltGr nor
+ // the super modifiers to clients then remove AltGr before passing
+ // the modifiers to onKey.
+ KeyModifierMask sendMask = (mask & ~KeyModifierAltGr);
+ if ((mask & KeyModifierAltGr) != 0) {
+ sendMask &= ~KeyModifierSuper;
+ }
+ mask &= ~KeyModifierAltGr;
+
+ // update button state
+ if (down) {
+ m_keyState->onKey(button, true, mask);
+ }
+ else if (up) {
+ if (!m_keyState->isKeyDown(button)) {
+ // up event for a dead key. throw it away.
+ return false;
+ }
+ m_keyState->onKey(button, false, mask);
+ }
+
+ // send key events
+ for (OSXKeyState::KeyIDs::const_iterator i = keys.begin();
+ i != keys.end(); ++i) {
+ m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat,
+ *i, sendMask, 1, button);
+ }
+
+ return true;
+}
+
+void
+OSXScreen::onMediaKey(CGEventRef event)
+{
+ KeyID keyID;
+ bool down;
+ bool isRepeat;
+
+ if (!getMediaKeyEventInfo (event, &keyID, &down, &isRepeat)) {
+ LOG ((CLOG_ERR "Failed to decode media key event"));
+ return;
+ }
+
+ LOG ((CLOG_DEBUG2 "Media key event: keyID=0x%02x, %s, repeat=%s",
+ keyID, (down ? "down": "up"),
+ (isRepeat ? "yes" : "no")));
+
+ KeyButton button = 0;
+ KeyModifierMask mask = m_keyState->getActiveModifiers();
+ m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, keyID, mask, 1, button);
+}
+
+bool
+OSXScreen::onHotKey(EventRef event) const
+{
+ // get the hotkey id
+ EventHotKeyID hkid;
+ GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID,
+ NULL, sizeof(EventHotKeyID), NULL, &hkid);
+ UInt32 id = hkid.id;
+
+ // determine event type
+ Event::Type type;
+ UInt32 eventKind = GetEventKind(event);
+ if (eventKind == kEventHotKeyPressed) {
+ type = m_events->forIPrimaryScreen().hotKeyDown();
+ }
+ else if (eventKind == kEventHotKeyReleased) {
+ type = m_events->forIPrimaryScreen().hotKeyUp();
+ }
+ else {
+ return false;
+ }
+
+ m_events->addEvent(Event(type, getEventTarget(),
+ HotKeyInfo::alloc(id)));
+
+ return true;
+}
+
+ButtonID
+OSXScreen::mapBarrierButtonToMac(UInt16 button) const
+{
+ switch (button) {
+ case 1:
+ return kButtonLeft;
+ case 2:
+ return kMacButtonMiddle;
+ case 3:
+ return kMacButtonRight;
+ }
+
+ return static_cast<ButtonID>(button);
+}
+
+ButtonID
+OSXScreen::mapMacButtonToBarrier(UInt16 macButton) const
+{
+ switch (macButton) {
+ case 1:
+ return kButtonLeft;
+
+ case 2:
+ return kButtonRight;
+
+ case 3:
+ return kButtonMiddle;
+ }
+
+ return static_cast<ButtonID>(macButton);
+}
+
+SInt32
+OSXScreen::mapScrollWheelToBarrier(SInt32 x) const
+{
+ // return accelerated scrolling but not exponentially scaled as it is
+ // on the mac.
+ double d = (1.0 + getScrollSpeed()) * x / getScrollSpeedFactor();
+ return static_cast<SInt32>(120.0 * d);
+}
+
+SInt32
+OSXScreen::mapScrollWheelFromBarrier(SInt32 x) const
+{
+ // use server's acceleration with a little boost since other platforms
+ // take one wheel step as a larger step than the mac does.
+ return static_cast<SInt32>(3.0 * x / 120.0);
+}
+
+double
+OSXScreen::getScrollSpeed() const
+{
+ double scaling = 0.0;
+
+ CFPropertyListRef pref = ::CFPreferencesCopyValue(
+ CFSTR("com.apple.scrollwheel.scaling") ,
+ kCFPreferencesAnyApplication,
+ kCFPreferencesCurrentUser,
+ kCFPreferencesAnyHost);
+ if (pref != NULL) {
+ CFTypeID id = CFGetTypeID(pref);
+ if (id == CFNumberGetTypeID()) {
+ CFNumberRef value = static_cast<CFNumberRef>(pref);
+ if (CFNumberGetValue(value, kCFNumberDoubleType, &scaling)) {
+ if (scaling < 0.0) {
+ scaling = 0.0;
+ }
+ }
+ }
+ CFRelease(pref);
+ }
+
+ return scaling;
+}
+
+double
+OSXScreen::getScrollSpeedFactor() const
+{
+ return pow(10.0, getScrollSpeed());
+}
+
+void
+OSXScreen::enableDragTimer(bool enable)
+{
+ if (enable && m_dragTimer == NULL) {
+ m_dragTimer = m_events->newTimer(0.01, NULL);
+ m_events->adoptHandler(Event::kTimer, m_dragTimer,
+ new TMethodEventJob<OSXScreen>(this,
+ &OSXScreen::handleDrag));
+ CGEventRef event = CGEventCreate(NULL);
+ CGPoint mouse = CGEventGetLocation(event);
+ m_dragLastPoint.h = (short)mouse.x;
+ m_dragLastPoint.v = (short)mouse.y;
+ CFRelease(event);
+ }
+ else if (!enable && m_dragTimer != NULL) {
+ m_events->removeHandler(Event::kTimer, m_dragTimer);
+ m_events->deleteTimer(m_dragTimer);
+ m_dragTimer = NULL;
+ }
+}
+
+void
+OSXScreen::handleDrag(const Event&, void*)
+{
+ CGEventRef event = CGEventCreate(NULL);
+ CGPoint p = CGEventGetLocation(event);
+ CFRelease(event);
+
+ if ((short)p.x != m_dragLastPoint.h || (short)p.y != m_dragLastPoint.v) {
+ m_dragLastPoint.h = (short)p.x;
+ m_dragLastPoint.v = (short)p.y;
+ onMouseMove((SInt32)p.x, (SInt32)p.y);
+ }
+}
+
+void
+OSXScreen::updateButtons()
+{
+ UInt32 buttons = GetCurrentButtonState();
+
+ m_buttonState.overwrite(buttons);
+}
+
+IKeyState*
+OSXScreen::getKeyState() const
+{
+ return m_keyState;
+}
+
+void
+OSXScreen::updateScreenShape(const CGDirectDisplayID, const CGDisplayChangeSummaryFlags flags)
+{
+ updateScreenShape();
+}
+
+void
+OSXScreen::updateScreenShape()
+{
+ // get info for each display
+ CGDisplayCount displayCount = 0;
+
+ if (CGGetActiveDisplayList(0, NULL, &displayCount) != CGDisplayNoErr) {
+ return;
+ }
+
+ if (displayCount == 0) {
+ return;
+ }
+
+ CGDirectDisplayID* displays = new CGDirectDisplayID[displayCount];
+ if (displays == NULL) {
+ return;
+ }
+
+ if (CGGetActiveDisplayList(displayCount,
+ displays, &displayCount) != CGDisplayNoErr) {
+ delete[] displays;
+ return;
+ }
+
+ // get smallest rect enclosing all display rects
+ CGRect totalBounds = CGRectZero;
+ for (CGDisplayCount i = 0; i < displayCount; ++i) {
+ CGRect bounds = CGDisplayBounds(displays[i]);
+ totalBounds = CGRectUnion(totalBounds, bounds);
+ }
+
+ // get shape of default screen
+ m_x = (SInt32)totalBounds.origin.x;
+ m_y = (SInt32)totalBounds.origin.y;
+ m_w = (SInt32)totalBounds.size.width;
+ m_h = (SInt32)totalBounds.size.height;
+
+ // get center of default screen
+ CGDirectDisplayID main = CGMainDisplayID();
+ const CGRect rect = CGDisplayBounds(main);
+ m_xCenter = (rect.origin.x + rect.size.width) / 2;
+ m_yCenter = (rect.origin.y + rect.size.height) / 2;
+
+ delete[] displays;
+ // We want to notify the peer screen whether we are primary screen or not
+ sendEvent(m_events->forIScreen().shapeChanged());
+
+ LOG((CLOG_DEBUG "screen shape: center=%d,%d size=%dx%d on %u %s",
+ m_x, m_y, m_w, m_h, displayCount,
+ (displayCount == 1) ? "display" : "displays"));
+}
+
+#pragma mark -
+
+//
+// FAST USER SWITCH NOTIFICATION SUPPORT
+//
+// OSXScreen::userSwitchCallback(void*)
+//
+// gets called if a fast user switch occurs
+//
+
+pascal OSStatus
+OSXScreen::userSwitchCallback(EventHandlerCallRef nextHandler,
+ EventRef theEvent,
+ void* inUserData)
+{
+ OSXScreen* screen = (OSXScreen*)inUserData;
+ UInt32 kind = GetEventKind(theEvent);
+ IEventQueue* events = screen->getEvents();
+
+ if (kind == kEventSystemUserSessionDeactivated) {
+ LOG((CLOG_DEBUG "user session deactivated"));
+ events->addEvent(Event(events->forIScreen().suspend(),
+ screen->getEventTarget()));
+ }
+ else if (kind == kEventSystemUserSessionActivated) {
+ LOG((CLOG_DEBUG "user session activated"));
+ events->addEvent(Event(events->forIScreen().resume(),
+ screen->getEventTarget()));
+ }
+ return (CallNextEventHandler(nextHandler, theEvent));
+}
+
+#pragma mark -
+
+//
+// SLEEP/WAKEUP NOTIFICATION SUPPORT
+//
+// OSXScreen::watchSystemPowerThread(void*)
+//
+// main of thread monitoring system power (sleep/wakup) using a CFRunLoop
+//
+
+void
+OSXScreen::watchSystemPowerThread(void*)
+{
+ io_object_t notifier;
+ IONotificationPortRef notificationPortRef;
+ CFRunLoopSourceRef runloopSourceRef = 0;
+
+ m_pmRunloop = CFRunLoopGetCurrent();
+ // install system power change callback
+ m_pmRootPort = IORegisterForSystemPower(this, &notificationPortRef,
+ powerChangeCallback, &notifier);
+ if (m_pmRootPort == 0) {
+ LOG((CLOG_WARN "IORegisterForSystemPower failed"));
+ }
+ else {
+ runloopSourceRef =
+ IONotificationPortGetRunLoopSource(notificationPortRef);
+ CFRunLoopAddSource(m_pmRunloop, runloopSourceRef,
+ kCFRunLoopCommonModes);
+ }
+
+ // thread is ready
+ {
+ Lock lock(m_pmMutex);
+ *m_pmThreadReady = true;
+ m_pmThreadReady->signal();
+ }
+
+ // if we were unable to initialize then exit. we must do this after
+ // setting m_pmThreadReady to true otherwise the parent thread will
+ // block waiting for it.
+ if (m_pmRootPort == 0) {
+ LOG((CLOG_WARN "failed to init watchSystemPowerThread"));
+ return;
+ }
+
+ LOG((CLOG_DEBUG "started watchSystemPowerThread"));
+
+ LOG((CLOG_DEBUG "waiting for event loop"));
+ m_events->waitForReady();
+
+#if defined(MAC_OS_X_VERSION_10_7)
+ {
+ Lock lockCarbon(m_carbonLoopMutex);
+ if (*m_carbonLoopReady == false) {
+
+ // we signalling carbon loop ready before starting
+ // unless we know how to do it within the loop
+ LOG((CLOG_DEBUG "signalling carbon loop ready"));
+
+ *m_carbonLoopReady = true;
+ m_carbonLoopReady->signal();
+ }
+ }
+#endif
+
+ // start the run loop
+ LOG((CLOG_DEBUG "starting carbon loop"));
+ CFRunLoopRun();
+ LOG((CLOG_DEBUG "carbon loop has stopped"));
+
+ // cleanup
+ if (notificationPortRef) {
+ CFRunLoopRemoveSource(m_pmRunloop,
+ runloopSourceRef, kCFRunLoopDefaultMode);
+ CFRunLoopSourceInvalidate(runloopSourceRef);
+ CFRelease(runloopSourceRef);
+ }
+
+ Lock lock(m_pmMutex);
+ IODeregisterForSystemPower(&notifier);
+ m_pmRootPort = 0;
+ LOG((CLOG_DEBUG "stopped watchSystemPowerThread"));
+}
+
+void
+OSXScreen::powerChangeCallback(void* refcon, io_service_t service,
+ natural_t messageType, void* messageArg)
+{
+ ((OSXScreen*)refcon)->handlePowerChangeRequest(messageType, messageArg);
+}
+
+void
+OSXScreen::handlePowerChangeRequest(natural_t messageType, void* messageArg)
+{
+ // we've received a power change notification
+ switch (messageType) {
+ case kIOMessageSystemWillSleep:
+ // OSXScreen has to handle this in the main thread so we have to
+ // queue a confirm sleep event here. we actually don't allow the
+ // system to sleep until the event is handled.
+ m_events->addEvent(Event(m_events->forOSXScreen().confirmSleep(),
+ getEventTarget(), messageArg,
+ Event::kDontFreeData));
+ return;
+
+ case kIOMessageSystemHasPoweredOn:
+ LOG((CLOG_DEBUG "system wakeup"));
+ m_events->addEvent(Event(m_events->forIScreen().resume(),
+ getEventTarget()));
+ break;
+
+ default:
+ break;
+ }
+
+ Lock lock(m_pmMutex);
+ if (m_pmRootPort != 0) {
+ IOAllowPowerChange(m_pmRootPort, (long)messageArg);
+ }
+}
+
+void
+OSXScreen::handleConfirmSleep(const Event& event, void*)
+{
+ long messageArg = (long)event.getData();
+ if (messageArg != 0) {
+ Lock lock(m_pmMutex);
+ if (m_pmRootPort != 0) {
+ // deliver suspend event immediately.
+ m_events->addEvent(Event(m_events->forIScreen().suspend(),
+ getEventTarget(), NULL,
+ Event::kDeliverImmediately));
+
+ LOG((CLOG_DEBUG "system will sleep"));
+ IOAllowPowerChange(m_pmRootPort, messageArg);
+ }
+ }
+}
+
+#pragma mark -
+
+//
+// GLOBAL HOTKEY OPERATING MODE SUPPORT (10.3)
+//
+// CoreGraphics private API (OSX 10.3)
+// Source: http://ichiro.nnip.org/osx/Cocoa/GlobalHotkey.html
+//
+// We load the functions dynamically because they're not available in
+// older SDKs. We don't use weak linking because we want users of
+// older SDKs to build an app that works on newer systems and older
+// SDKs will not provide the symbols.
+//
+// FIXME: This is hosed as of OS 10.5; patches to repair this are
+// a good thing.
+//
+#if 0
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int CGSConnection;
+typedef enum {
+ CGSGlobalHotKeyEnable = 0,
+ CGSGlobalHotKeyDisable = 1,
+} CGSGlobalHotKeyOperatingMode;
+
+extern CGSConnection _CGSDefaultConnection(void) WEAK_IMPORT_ATTRIBUTE;
+extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode *mode) WEAK_IMPORT_ATTRIBUTE;
+extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode) WEAK_IMPORT_ATTRIBUTE;
+
+typedef CGSConnection (*_CGSDefaultConnection_t)(void);
+typedef CGError (*CGSGetGlobalHotKeyOperatingMode_t)(CGSConnection connection, CGSGlobalHotKeyOperatingMode *mode);
+typedef CGError (*CGSSetGlobalHotKeyOperatingMode_t)(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode);
+
+static _CGSDefaultConnection_t s__CGSDefaultConnection;
+static CGSGetGlobalHotKeyOperatingMode_t s_CGSGetGlobalHotKeyOperatingMode;
+static CGSSetGlobalHotKeyOperatingMode_t s_CGSSetGlobalHotKeyOperatingMode;
+
+#ifdef __cplusplus
+}
+#endif
+
+#define LOOKUP(name_) \
+ s_ ## name_ = NULL; \
+ if (NSIsSymbolNameDefinedWithHint("_" #name_, "CoreGraphics")) { \
+ s_ ## name_ = (name_ ## _t)NSAddressOfSymbol( \
+ NSLookupAndBindSymbolWithHint( \
+ "_" #name_, "CoreGraphics")); \
+ }
+
+bool
+OSXScreen::isGlobalHotKeyOperatingModeAvailable()
+{
+ if (!s_testedForGHOM) {
+ s_testedForGHOM = true;
+ LOOKUP(_CGSDefaultConnection);
+ LOOKUP(CGSGetGlobalHotKeyOperatingMode);
+ LOOKUP(CGSSetGlobalHotKeyOperatingMode);
+ s_hasGHOM = (s__CGSDefaultConnection != NULL &&
+ s_CGSGetGlobalHotKeyOperatingMode != NULL &&
+ s_CGSSetGlobalHotKeyOperatingMode != NULL);
+ }
+ return s_hasGHOM;
+}
+
+void
+OSXScreen::setGlobalHotKeysEnabled(bool enabled)
+{
+ if (isGlobalHotKeyOperatingModeAvailable()) {
+ CGSConnection conn = s__CGSDefaultConnection();
+
+ CGSGlobalHotKeyOperatingMode mode;
+ s_CGSGetGlobalHotKeyOperatingMode(conn, &mode);
+
+ if (enabled && mode == CGSGlobalHotKeyDisable) {
+ s_CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyEnable);
+ }
+ else if (!enabled && mode == CGSGlobalHotKeyEnable) {
+ s_CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyDisable);
+ }
+ }
+}
+
+bool
+OSXScreen::getGlobalHotKeysEnabled()
+{
+ CGSGlobalHotKeyOperatingMode mode;
+ if (isGlobalHotKeyOperatingModeAvailable()) {
+ CGSConnection conn = s__CGSDefaultConnection();
+ s_CGSGetGlobalHotKeyOperatingMode(conn, &mode);
+ }
+ else {
+ mode = CGSGlobalHotKeyEnable;
+ }
+ return (mode == CGSGlobalHotKeyEnable);
+}
+
+#endif
+
+//
+// OSXScreen::HotKeyItem
+//
+
+OSXScreen::HotKeyItem::HotKeyItem(UInt32 keycode, UInt32 mask) :
+ m_ref(NULL),
+ m_keycode(keycode),
+ m_mask(mask)
+{
+ // do nothing
+}
+
+OSXScreen::HotKeyItem::HotKeyItem(EventHotKeyRef ref,
+ UInt32 keycode, UInt32 mask) :
+ m_ref(ref),
+ m_keycode(keycode),
+ m_mask(mask)
+{
+ // do nothing
+}
+
+EventHotKeyRef
+OSXScreen::HotKeyItem::getRef() const
+{
+ return m_ref;
+}
+
+bool
+OSXScreen::HotKeyItem::operator<(const HotKeyItem& x) const
+{
+ return (m_keycode < x.m_keycode ||
+ (m_keycode == x.m_keycode && m_mask < x.m_mask));
+}
+
+// Quartz event tap support for the secondary display. This makes sure that we
+// will show the cursor if a local event comes in while barrier has the cursor
+// off the screen.
+CGEventRef
+OSXScreen::handleCGInputEventSecondary(
+ CGEventTapProxy proxy,
+ CGEventType type,
+ CGEventRef event,
+ void* refcon)
+{
+ // this fix is really screwing with the correct show/hide behavior. it
+ // should be tested better before reintroducing.
+ return event;
+
+ OSXScreen* screen = (OSXScreen*)refcon;
+ if (screen->m_cursorHidden && type == kCGEventMouseMoved) {
+
+ CGPoint pos = CGEventGetLocation(event);
+ if (pos.x != screen->m_xCenter || pos.y != screen->m_yCenter) {
+
+ LOG((CLOG_DEBUG "show cursor on secondary, type=%d pos=%d,%d",
+ type, pos.x, pos.y));
+ screen->showCursor();
+ }
+ }
+ return event;
+}
+
+// Quartz event tap support
+CGEventRef
+OSXScreen::handleCGInputEvent(CGEventTapProxy proxy,
+ CGEventType type,
+ CGEventRef event,
+ void* refcon)
+{
+ OSXScreen* screen = (OSXScreen*)refcon;
+ CGPoint pos;
+
+ switch(type) {
+ case kCGEventLeftMouseDown:
+ case kCGEventRightMouseDown:
+ case kCGEventOtherMouseDown:
+ screen->onMouseButton(true, CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber) + 1);
+ break;
+ case kCGEventLeftMouseUp:
+ case kCGEventRightMouseUp:
+ case kCGEventOtherMouseUp:
+ screen->onMouseButton(false, CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber) + 1);
+ break;
+ case kCGEventLeftMouseDragged:
+ case kCGEventRightMouseDragged:
+ case kCGEventOtherMouseDragged:
+ case kCGEventMouseMoved:
+ pos = CGEventGetLocation(event);
+ screen->onMouseMove(pos.x, pos.y);
+
+ // The system ignores our cursor-centering calls if
+ // we don't return the event. This should be harmless,
+ // but might register as slight movement to other apps
+ // on the system. It hasn't been a problem before, though.
+ return event;
+ break;
+ case kCGEventScrollWheel:
+ screen->onMouseWheel(screen->mapScrollWheelToBarrier(
+ CGEventGetIntegerValueField(event, kCGScrollWheelEventDeltaAxis2)),
+ screen->mapScrollWheelToBarrier(
+ CGEventGetIntegerValueField(event, kCGScrollWheelEventDeltaAxis1)));
+ break;
+ case kCGEventKeyDown:
+ case kCGEventKeyUp:
+ case kCGEventFlagsChanged:
+ screen->onKey(event);
+ break;
+ case kCGEventTapDisabledByTimeout:
+ // Re-enable our event-tap
+ CGEventTapEnable(screen->m_eventTapPort, true);
+ LOG((CLOG_INFO "quartz event tap was disabled by timeout, re-enabling"));
+ break;
+ case kCGEventTapDisabledByUserInput:
+ LOG((CLOG_ERR "quartz event tap was disabled by user input"));
+ break;
+ case NX_NULLEVENT:
+ break;
+ default:
+ if (type == NX_SYSDEFINED) {
+ if (isMediaKeyEvent (event)) {
+ LOG((CLOG_DEBUG2 "detected media key event"));
+ screen->onMediaKey (event);
+ } else {
+ LOG((CLOG_DEBUG2 "ignoring unknown system defined event"));
+ return event;
+ }
+ break;
+ }
+
+ LOG((CLOG_DEBUG3 "unknown quartz event type: 0x%02x", type));
+ }
+
+ if (screen->m_isOnScreen) {
+ return event;
+ } else {
+ return NULL;
+ }
+}
+
+void
+OSXScreen::MouseButtonState::set(UInt32 button, EMouseButtonState state)
+{
+ bool newState = (state == kMouseButtonDown);
+ m_buttons.set(button, newState);
+}
+
+bool
+OSXScreen::MouseButtonState::any()
+{
+ return m_buttons.any();
+}
+
+void
+OSXScreen::MouseButtonState::reset()
+{
+ m_buttons.reset();
+}
+
+void
+OSXScreen::MouseButtonState::overwrite(UInt32 buttons)
+{
+ m_buttons = std::bitset<NumButtonIDs>(buttons);
+}
+
+bool
+OSXScreen::MouseButtonState::test(UInt32 button) const
+{
+ return m_buttons.test(button);
+}
+
+SInt8
+OSXScreen::MouseButtonState::getFirstButtonDown() const
+{
+ if (m_buttons.any()) {
+ for (unsigned short button = 0; button < m_buttons.size(); button++) {
+ if (m_buttons.test(button)) {
+ return button;
+ }
+ }
+ }
+ return -1;
+}
+
+char*
+OSXScreen::CFStringRefToUTF8String(CFStringRef aString)
+{
+ if (aString == NULL) {
+ return NULL;
+ }
+
+ CFIndex length = CFStringGetLength(aString);
+ CFIndex maxSize = CFStringGetMaximumSizeForEncoding(
+ length,
+ kCFStringEncodingUTF8);
+ char* buffer = (char*)malloc(maxSize);
+ if (CFStringGetCString(aString, buffer, maxSize, kCFStringEncodingUTF8)) {
+ return buffer;
+ }
+ return NULL;
+}
+
+void
+OSXScreen::fakeDraggingFiles(DragFileList fileList)
+{
+ m_fakeDraggingStarted = true;
+ String fileExt;
+ if (fileList.size() == 1) {
+ fileExt = DragInformation::getDragFileExtension(
+ fileList.at(0).getFilename());
+ }
+
+#if defined(MAC_OS_X_VERSION_10_7)
+ fakeDragging(fileExt.c_str(), m_xCursor, m_yCursor);
+#else
+ LOG((CLOG_WARN "drag drop not supported"));
+#endif
+}
+
+String&
+OSXScreen::getDraggingFilename()
+{
+ if (m_draggingStarted) {
+ CFStringRef dragInfo = getDraggedFileURL();
+ char* info = NULL;
+ info = CFStringRefToUTF8String(dragInfo);
+ if (info == NULL) {
+ m_draggingFilename.clear();
+ }
+ else {
+ LOG((CLOG_DEBUG "drag info: %s", info));
+ CFRelease(dragInfo);
+ String fileList(info);
+ m_draggingFilename = fileList;
+ }
+
+ // fake a escape key down and up then left mouse button up
+ fakeKeyDown(kKeyEscape, 8192, 1);
+ fakeKeyUp(1);
+ fakeMouseButton(kButtonLeft, false);
+ }
+ return m_draggingFilename;
+}
+
+void
+OSXScreen::waitForCarbonLoop() const
+{
+#if defined(MAC_OS_X_VERSION_10_7)
+ if (*m_carbonLoopReady) {
+ LOG((CLOG_DEBUG "carbon loop already ready"));
+ return;
+ }
+
+ Lock lock(m_carbonLoopMutex);
+
+ LOG((CLOG_DEBUG "waiting for carbon loop"));
+
+ double timeout = ARCH->time() + kCarbonLoopWaitTimeout;
+ while (!m_carbonLoopReady->wait()) {
+ if (ARCH->time() > timeout) {
+ LOG((CLOG_DEBUG "carbon loop not ready, waiting again"));
+ timeout = ARCH->time() + kCarbonLoopWaitTimeout;
+ }
+ }
+
+ LOG((CLOG_DEBUG "carbon loop ready"));
+#endif
+
+}
+
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+void
+setZeroSuppressionInterval()
+{
+ CGSetLocalEventsSuppressionInterval(0.0);
+}
+
+void
+avoidSupression()
+{
+ // avoid suppression of local hardware events
+ // stkamp@users.sourceforge.net
+ CGSetLocalEventsFilterDuringSupressionState(
+ kCGEventFilterMaskPermitAllEvents,
+ kCGEventSupressionStateSupressionInterval);
+ CGSetLocalEventsFilterDuringSupressionState(
+ (kCGEventFilterMaskPermitLocalKeyboardEvents |
+ kCGEventFilterMaskPermitSystemDefinedEvents),
+ kCGEventSupressionStateRemoteMouseDrag);
+}
+
+void
+logCursorVisibility()
+{
+ // CGCursorIsVisible is probably deprecated because its unreliable.
+ if (!CGCursorIsVisible()) {
+ LOG((CLOG_WARN "cursor may not be visible"));
+ }
+}
+
+void
+avoidHesitatingCursor()
+{
+ // This used to be necessary to get smooth mouse motion on other screens,
+ // but now is just to avoid a hesitating cursor when transitioning to
+ // the primary (this) screen.
+ CGSetLocalEventsSuppressionInterval(0.0001);
+}
+
+#pragma GCC diagnostic error "-Wdeprecated-declarations"
diff --git a/src/lib/platform/OSXScreenSaver.cpp b/src/lib/platform/OSXScreenSaver.cpp
new file mode 100644
index 0000000..a0282d9
--- /dev/null
+++ b/src/lib/platform/OSXScreenSaver.cpp
@@ -0,0 +1,201 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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/>.
+ */
+
+#import "platform/OSXScreenSaver.h"
+
+#import "platform/OSXScreenSaverUtil.h"
+#import "barrier/IPrimaryScreen.h"
+#import "base/Log.h"
+#import "base/IEventQueue.h"
+
+#import <string.h>
+#import <sys/sysctl.h>
+
+// TODO: upgrade deprecated function usage in these functions.
+void getProcessSerialNumber(const char* name, ProcessSerialNumber& psn);
+bool testProcessName(const char* name, const ProcessSerialNumber& psn);
+
+//
+// OSXScreenSaver
+//
+
+OSXScreenSaver::OSXScreenSaver(IEventQueue* events, void* eventTarget) :
+ m_eventTarget(eventTarget),
+ m_enabled(true),
+ m_events(events)
+{
+ m_autoReleasePool = screenSaverUtilCreatePool();
+ m_screenSaverController = screenSaverUtilCreateController();
+
+ // install launch/termination event handlers
+ EventTypeSpec launchEventTypes[2];
+ launchEventTypes[0].eventClass = kEventClassApplication;
+ launchEventTypes[0].eventKind = kEventAppLaunched;
+ launchEventTypes[1].eventClass = kEventClassApplication;
+ launchEventTypes[1].eventKind = kEventAppTerminated;
+
+ EventHandlerUPP launchTerminationEventHandler =
+ NewEventHandlerUPP(launchTerminationCallback);
+ InstallApplicationEventHandler(launchTerminationEventHandler, 2,
+ launchEventTypes, this,
+ &m_launchTerminationEventHandlerRef);
+ DisposeEventHandlerUPP(launchTerminationEventHandler);
+
+ m_screenSaverPSN.highLongOfPSN = 0;
+ m_screenSaverPSN.lowLongOfPSN = 0;
+
+ if (isActive()) {
+ getProcessSerialNumber("ScreenSaverEngine", m_screenSaverPSN);
+ }
+}
+
+OSXScreenSaver::~OSXScreenSaver()
+{
+ RemoveEventHandler(m_launchTerminationEventHandlerRef);
+// screenSaverUtilReleaseController(m_screenSaverController);
+ screenSaverUtilReleasePool(m_autoReleasePool);
+}
+
+void
+OSXScreenSaver::enable()
+{
+ m_enabled = true;
+ screenSaverUtilEnable(m_screenSaverController);
+}
+
+void
+OSXScreenSaver::disable()
+{
+ m_enabled = false;
+ screenSaverUtilDisable(m_screenSaverController);
+}
+
+void
+OSXScreenSaver::activate()
+{
+ screenSaverUtilActivate(m_screenSaverController);
+}
+
+void
+OSXScreenSaver::deactivate()
+{
+ screenSaverUtilDeactivate(m_screenSaverController, m_enabled);
+}
+
+bool
+OSXScreenSaver::isActive() const
+{
+ return (screenSaverUtilIsActive(m_screenSaverController) != 0);
+}
+
+void
+OSXScreenSaver::processLaunched(ProcessSerialNumber psn)
+{
+ if (testProcessName("ScreenSaverEngine", psn)) {
+ m_screenSaverPSN = psn;
+ LOG((CLOG_DEBUG1 "ScreenSaverEngine launched. Enabled=%d", m_enabled));
+ if (m_enabled) {
+ m_events->addEvent(
+ Event(m_events->forIPrimaryScreen().screensaverActivated(),
+ m_eventTarget));
+ }
+ }
+}
+
+void
+OSXScreenSaver::processTerminated(ProcessSerialNumber psn)
+{
+ if (m_screenSaverPSN.highLongOfPSN == psn.highLongOfPSN &&
+ m_screenSaverPSN.lowLongOfPSN == psn.lowLongOfPSN) {
+ LOG((CLOG_DEBUG1 "ScreenSaverEngine terminated. Enabled=%d", m_enabled));
+ if (m_enabled) {
+ m_events->addEvent(
+ Event(m_events->forIPrimaryScreen().screensaverDeactivated(),
+ m_eventTarget));
+ }
+
+ m_screenSaverPSN.highLongOfPSN = 0;
+ m_screenSaverPSN.lowLongOfPSN = 0;
+ }
+}
+
+pascal OSStatus
+OSXScreenSaver::launchTerminationCallback(
+ EventHandlerCallRef nextHandler,
+ EventRef theEvent, void* userData)
+{
+ OSStatus result;
+ ProcessSerialNumber psn;
+ EventParamType actualType;
+ ByteCount actualSize;
+
+ result = GetEventParameter(theEvent, kEventParamProcessID,
+ typeProcessSerialNumber, &actualType,
+ sizeof(psn), &actualSize, &psn);
+
+ if ((result == noErr) &&
+ (actualSize > 0) &&
+ (actualType == typeProcessSerialNumber)) {
+ OSXScreenSaver* screenSaver = (OSXScreenSaver*)userData;
+ UInt32 eventKind = GetEventKind(theEvent);
+ if (eventKind == kEventAppLaunched) {
+ screenSaver->processLaunched(psn);
+ }
+ else if (eventKind == kEventAppTerminated) {
+ screenSaver->processTerminated(psn);
+ }
+ }
+ return (CallNextEventHandler(nextHandler, theEvent));
+}
+
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
+void
+getProcessSerialNumber(const char* name, ProcessSerialNumber& psn)
+{
+ ProcessInfoRec procInfo;
+ Str31 procName; // pascal string. first byte holds length.
+ memset(&procInfo, 0, sizeof(procInfo));
+ procInfo.processName = procName;
+ procInfo.processInfoLength = sizeof(ProcessInfoRec);
+
+ ProcessSerialNumber checkPsn;
+ OSErr err = GetNextProcess(&checkPsn);
+ while (err == 0) {
+ memset(procName, 0, sizeof(procName));
+ err = GetProcessInformation(&checkPsn, &procInfo);
+ if (err != 0) {
+ break;
+ }
+ if (strcmp(name, (const char*)&procName[1]) == 0) {
+ psn = checkPsn;
+ break;
+ }
+ err = GetNextProcess(&checkPsn);
+ }
+}
+
+bool
+testProcessName(const char* name, const ProcessSerialNumber& psn)
+{
+ CFStringRef processName;
+ OSStatus err = CopyProcessName(&psn, &processName);
+ return (err == 0 && CFEqual(CFSTR("ScreenSaverEngine"), processName));
+}
+
+#pragma GCC diagnostic error "-Wdeprecated-declarations"
diff --git a/src/lib/platform/OSXScreenSaver.h b/src/lib/platform/OSXScreenSaver.h
new file mode 100644
index 0000000..07f2a7b
--- /dev/null
+++ b/src/lib/platform/OSXScreenSaver.h
@@ -0,0 +1,59 @@
+/*
+ * barrier -- 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 "barrier/IScreenSaver.h"
+
+#include <Carbon/Carbon.h>
+
+class IEventQueue;
+
+//! OSX screen saver implementation
+class OSXScreenSaver : public IScreenSaver {
+public:
+ OSXScreenSaver(IEventQueue* events, void* eventTarget);
+ virtual ~OSXScreenSaver();
+
+ // IScreenSaver overrides
+ virtual void enable();
+ virtual void disable();
+ virtual void activate();
+ virtual void deactivate();
+ virtual bool isActive() const;
+
+private:
+ void processLaunched(ProcessSerialNumber psn);
+ void processTerminated(ProcessSerialNumber psn);
+
+ static pascal OSStatus
+ launchTerminationCallback(
+ EventHandlerCallRef nextHandler,
+ EventRef theEvent, void* userData);
+
+private:
+ // the target for the events we generate
+ void* m_eventTarget;
+
+ bool m_enabled;
+ void* m_screenSaverController;
+ void* m_autoReleasePool;
+ EventHandlerRef m_launchTerminationEventHandlerRef;
+ ProcessSerialNumber m_screenSaverPSN;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/platform/OSXScreenSaverControl.h b/src/lib/platform/OSXScreenSaverControl.h
new file mode 100644
index 0000000..76f8875
--- /dev/null
+++ b/src/lib/platform/OSXScreenSaverControl.h
@@ -0,0 +1,54 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2009 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/>.
+ */
+
+// ScreenSaver.framework private API
+// Class dumping by Alex Harper http://www.ragingmenace.com/
+
+#import <Foundation/NSObject.h>
+
+@protocol ScreenSaverControl
+- (double)screenSaverTimeRemaining;
+- (void)restartForUser:fp12;
+- (void)screenSaverStopNow;
+- (void)screenSaverStartNow;
+- (void)setScreenSaverCanRun:(char)fp12;
+- (BOOL)screenSaverCanRun;
+- (BOOL)screenSaverIsRunning;
+@end
+
+
+@interface ScreenSaverController:NSObject <ScreenSaverControl>
+
++ controller;
++ monitor;
++ daemonConnectionName;
++ daemonPath;
++ enginePath;
+- init;
+- (void)dealloc;
+- (void)_connectionClosed:fp12;
+- (BOOL)screenSaverIsRunning;
+- (BOOL)screenSaverCanRun;
+- (void)setScreenSaverCanRun:(char)fp12;
+- (void)screenSaverStartNow;
+- (void)screenSaverStopNow;
+- (void)restartForUser:fp12;
+- (double)screenSaverTimeRemaining;
+
+@end
+
diff --git a/src/lib/platform/OSXScreenSaverUtil.h b/src/lib/platform/OSXScreenSaverUtil.h
new file mode 100644
index 0000000..045553d
--- /dev/null
+++ b/src/lib/platform/OSXScreenSaverUtil.h
@@ -0,0 +1,40 @@
+/*
+ * barrier -- 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 "common/common.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void* screenSaverUtilCreatePool();
+void screenSaverUtilReleasePool(void*);
+
+void* screenSaverUtilCreateController();
+void screenSaverUtilReleaseController(void*);
+void screenSaverUtilEnable(void*);
+void screenSaverUtilDisable(void*);
+void screenSaverUtilActivate(void*);
+void screenSaverUtilDeactivate(void*, int isEnabled);
+int screenSaverUtilIsActive(void*);
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/src/lib/platform/OSXScreenSaverUtil.m b/src/lib/platform/OSXScreenSaverUtil.m
new file mode 100644
index 0000000..6d82f10
--- /dev/null
+++ b/src/lib/platform/OSXScreenSaverUtil.m
@@ -0,0 +1,83 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2004 Chris Schoeneman, Nick Bolton, Sorin Sbarnea
+ *
+ * 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.
+ */
+
+#import "platform/OSXScreenSaverUtil.h"
+
+#import "platform/OSXScreenSaverControl.h"
+
+#import <Foundation/NSAutoreleasePool.h>
+
+//
+// screenSaverUtil functions
+//
+// Note: these helper functions exist only so we can avoid using ObjC++.
+// autoconf/automake don't know about ObjC++ and I don't know how to
+// teach them about it.
+//
+
+void*
+screenSaverUtilCreatePool()
+{
+ return [[NSAutoreleasePool alloc] init];
+}
+
+void
+screenSaverUtilReleasePool(void* pool)
+{
+ [(NSAutoreleasePool*)pool release];
+}
+
+void*
+screenSaverUtilCreateController()
+{
+ return [[ScreenSaverController controller] retain];
+}
+
+void
+screenSaverUtilReleaseController(void* controller)
+{
+ [(ScreenSaverController*)controller release];
+}
+
+void
+screenSaverUtilEnable(void* controller)
+{
+ [(ScreenSaverController*)controller setScreenSaverCanRun:YES];
+}
+
+void
+screenSaverUtilDisable(void* controller)
+{
+ [(ScreenSaverController*)controller setScreenSaverCanRun:NO];
+}
+
+void
+screenSaverUtilActivate(void* controller)
+{
+ [(ScreenSaverController*)controller setScreenSaverCanRun:YES];
+ [(ScreenSaverController*)controller screenSaverStartNow];
+}
+
+void
+screenSaverUtilDeactivate(void* controller, int isEnabled)
+{
+ [(ScreenSaverController*)controller screenSaverStopNow];
+ [(ScreenSaverController*)controller setScreenSaverCanRun:isEnabled];
+}
+
+int
+screenSaverUtilIsActive(void* controller)
+{
+ return [(ScreenSaverController*)controller screenSaverIsRunning];
+}
diff --git a/src/lib/platform/OSXUchrKeyResource.cpp b/src/lib/platform/OSXUchrKeyResource.cpp
new file mode 100644
index 0000000..e0230e9
--- /dev/null
+++ b/src/lib/platform/OSXUchrKeyResource.cpp
@@ -0,0 +1,296 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 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 "platform/OSXUchrKeyResource.h"
+
+#include <Carbon/Carbon.h>
+
+//
+// OSXUchrKeyResource
+//
+
+OSXUchrKeyResource::OSXUchrKeyResource(const void* resource,
+ UInt32 keyboardType) :
+ m_m(NULL),
+ m_cti(NULL),
+ m_sdi(NULL),
+ m_sri(NULL),
+ m_st(NULL)
+{
+ m_resource = static_cast<const UCKeyboardLayout*>(resource);
+ if (m_resource == NULL) {
+ return;
+ }
+
+ // find the keyboard info for the current keyboard type
+ const UCKeyboardTypeHeader* th = NULL;
+ const UCKeyboardLayout* r = m_resource;
+ for (ItemCount i = 0; i < r->keyboardTypeCount; ++i) {
+ if (keyboardType >= r->keyboardTypeList[i].keyboardTypeFirst &&
+ keyboardType <= r->keyboardTypeList[i].keyboardTypeLast) {
+ th = r->keyboardTypeList + i;
+ break;
+ }
+ if (r->keyboardTypeList[i].keyboardTypeFirst == 0) {
+ // found the default. use it unless we find a match.
+ th = r->keyboardTypeList + i;
+ }
+ }
+ if (th == NULL) {
+ // cannot find a suitable keyboard type
+ return;
+ }
+
+ // get tables for keyboard type
+ const UInt8* const base = reinterpret_cast<const UInt8*>(m_resource);
+ m_m = reinterpret_cast<const UCKeyModifiersToTableNum*>(base +
+ th->keyModifiersToTableNumOffset);
+ m_cti = reinterpret_cast<const UCKeyToCharTableIndex*>(base +
+ th->keyToCharTableIndexOffset);
+ m_sdi = reinterpret_cast<const UCKeySequenceDataIndex*>(base +
+ th->keySequenceDataIndexOffset);
+ if (th->keyStateRecordsIndexOffset != 0) {
+ m_sri = reinterpret_cast<const UCKeyStateRecordsIndex*>(base +
+ th->keyStateRecordsIndexOffset);
+ }
+ if (th->keyStateTerminatorsOffset != 0) {
+ m_st = reinterpret_cast<const UCKeyStateTerminators*>(base +
+ th->keyStateTerminatorsOffset);
+ }
+
+ // find the space key, but only if it can combine with dead keys.
+ // a dead key followed by a space yields the non-dead version of
+ // the dead key.
+ m_spaceOutput = 0xffffu;
+ UInt32 table = getTableForModifier(0);
+ for (UInt32 button = 0, n = getNumButtons(); button < n; ++button) {
+ KeyID id = getKey(table, button);
+ if (id == 0x20) {
+ UCKeyOutput c =
+ reinterpret_cast<const UCKeyOutput*>(base +
+ m_cti->keyToCharTableOffsets[table])[button];
+ if ((c & kUCKeyOutputTestForIndexMask) ==
+ kUCKeyOutputStateIndexMask) {
+ m_spaceOutput = (c & kUCKeyOutputGetIndexMask);
+ break;
+ }
+ }
+ }
+}
+
+bool
+OSXUchrKeyResource::isValid() const
+{
+ return (m_m != NULL);
+}
+
+UInt32
+OSXUchrKeyResource::getNumModifierCombinations() const
+{
+ // only 32 (not 256) because the righthanded modifier bits are ignored
+ return 32;
+}
+
+UInt32
+OSXUchrKeyResource::getNumTables() const
+{
+ return m_cti->keyToCharTableCount;
+}
+
+UInt32
+OSXUchrKeyResource::getNumButtons() const
+{
+ return m_cti->keyToCharTableSize;
+}
+
+UInt32
+OSXUchrKeyResource::getTableForModifier(UInt32 mask) const
+{
+ if (mask >= m_m->modifiersCount) {
+ return m_m->defaultTableNum;
+ }
+ else {
+ return m_m->tableNum[mask];
+ }
+}
+
+KeyID
+OSXUchrKeyResource::getKey(UInt32 table, UInt32 button) const
+{
+ assert(table < getNumTables());
+ assert(button < getNumButtons());
+
+ const UInt8* const base = reinterpret_cast<const UInt8*>(m_resource);
+ const UCKeyOutput* cPtr = reinterpret_cast<const UCKeyOutput*>(base +
+ m_cti->keyToCharTableOffsets[table]);
+
+ const UCKeyOutput c = cPtr[button];
+
+ KeySequence keys;
+ switch (c & kUCKeyOutputTestForIndexMask) {
+ case kUCKeyOutputStateIndexMask:
+ if (!getDeadKey(keys, c & kUCKeyOutputGetIndexMask)) {
+ return kKeyNone;
+ }
+ break;
+
+ case kUCKeyOutputSequenceIndexMask:
+ default:
+ if (!addSequence(keys, c)) {
+ return kKeyNone;
+ }
+ break;
+ }
+
+ // XXX -- no support for multiple characters
+ if (keys.size() != 1) {
+ return kKeyNone;
+ }
+
+ return keys.front();
+}
+
+bool
+OSXUchrKeyResource::getDeadKey(
+ KeySequence& keys, UInt16 index) const
+{
+ if (m_sri == NULL || index >= m_sri->keyStateRecordCount) {
+ // XXX -- should we be using some other fallback?
+ return false;
+ }
+
+ UInt16 state = 0;
+ if (!getKeyRecord(keys, index, state)) {
+ return false;
+ }
+ if (state == 0) {
+ // not a dead key
+ return true;
+ }
+
+ // no dead keys if we couldn't find the space key
+ if (m_spaceOutput == 0xffffu) {
+ return false;
+ }
+
+ // the dead key should not have put anything in the key list
+ if (!keys.empty()) {
+ return false;
+ }
+
+ // get the character generated by pressing the space key after the
+ // dead key. if we're still in a compose state afterwards then we're
+ // confused so we bail.
+ if (!getKeyRecord(keys, m_spaceOutput, state) || state != 0) {
+ return false;
+ }
+
+ // convert keys to their dead counterparts
+ for (KeySequence::iterator i = keys.begin(); i != keys.end(); ++i) {
+ *i = barrier::KeyMap::getDeadKey(*i);
+ }
+
+ return true;
+}
+
+bool
+OSXUchrKeyResource::getKeyRecord(
+ KeySequence& keys, UInt16 index, UInt16& state) const
+{
+ const UInt8* const base = reinterpret_cast<const UInt8*>(m_resource);
+ const UCKeyStateRecord* sr =
+ reinterpret_cast<const UCKeyStateRecord*>(base +
+ m_sri->keyStateRecordOffsets[index]);
+ const UCKeyStateEntryTerminal* kset =
+ reinterpret_cast<const UCKeyStateEntryTerminal*>(sr->stateEntryData);
+
+ UInt16 nextState = 0;
+ bool found = false;
+ if (state == 0) {
+ found = true;
+ nextState = sr->stateZeroNextState;
+ if (!addSequence(keys, sr->stateZeroCharData)) {
+ return false;
+ }
+ }
+ else {
+ // we have a next entry
+ switch (sr->stateEntryFormat) {
+ case kUCKeyStateEntryTerminalFormat:
+ for (UInt16 j = 0; j < sr->stateEntryCount; ++j) {
+ if (kset[j].curState == state) {
+ if (!addSequence(keys, kset[j].charData)) {
+ return false;
+ }
+ nextState = 0;
+ found = true;
+ break;
+ }
+ }
+ break;
+
+ case kUCKeyStateEntryRangeFormat:
+ // XXX -- not supported yet
+ break;
+
+ default:
+ // XXX -- unknown format
+ return false;
+ }
+ }
+ if (!found) {
+ // use a terminator
+ if (m_st != NULL && state < m_st->keyStateTerminatorCount) {
+ if (!addSequence(keys, m_st->keyStateTerminators[state - 1])) {
+ return false;
+ }
+ }
+ nextState = sr->stateZeroNextState;
+ if (!addSequence(keys, sr->stateZeroCharData)) {
+ return false;
+ }
+ }
+
+ // next
+ state = nextState;
+
+ return true;
+}
+
+bool
+OSXUchrKeyResource::addSequence(
+ KeySequence& keys, UCKeyCharSeq c) const
+{
+ if ((c & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) {
+ UInt16 index = (c & kUCKeyOutputGetIndexMask);
+ if (index < m_sdi->charSequenceCount &&
+ m_sdi->charSequenceOffsets[index] !=
+ m_sdi->charSequenceOffsets[index + 1]) {
+ // XXX -- sequences not supported yet
+ return false;
+ }
+ }
+
+ if (c != 0xfffe && c != 0xffff) {
+ KeyID id = unicharToKeyID(c);
+ if (id != kKeyNone) {
+ keys.push_back(id);
+ }
+ }
+
+ return true;
+}
diff --git a/src/lib/platform/OSXUchrKeyResource.h b/src/lib/platform/OSXUchrKeyResource.h
new file mode 100644
index 0000000..47b63c9
--- /dev/null
+++ b/src/lib/platform/OSXUchrKeyResource.h
@@ -0,0 +1,55 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 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/>.
+ */
+
+#pragma once
+
+#include "barrier/KeyState.h"
+#include "platform/IOSXKeyResource.h"
+
+#include <Carbon/Carbon.h>
+
+typedef TISInputSourceRef KeyLayout;
+
+class OSXUchrKeyResource : public IOSXKeyResource {
+public:
+ OSXUchrKeyResource(const void*, UInt32 keyboardType);
+
+ // KeyResource overrides
+ virtual bool isValid() const;
+ virtual UInt32 getNumModifierCombinations() const;
+ virtual UInt32 getNumTables() const;
+ virtual UInt32 getNumButtons() const;
+ virtual UInt32 getTableForModifier(UInt32 mask) const;
+ virtual KeyID getKey(UInt32 table, UInt32 button) const;
+
+private:
+ typedef std::vector<KeyID> KeySequence;
+
+ bool getDeadKey(KeySequence& keys, UInt16 index) const;
+ bool getKeyRecord(KeySequence& keys,
+ UInt16 index, UInt16& state) const;
+ bool addSequence(KeySequence& keys, UCKeyCharSeq c) const;
+
+private:
+ const UCKeyboardLayout* m_resource;
+ const UCKeyModifiersToTableNum* m_m;
+ const UCKeyToCharTableIndex* m_cti;
+ const UCKeySequenceDataIndex* m_sdi;
+ const UCKeyStateRecordsIndex* m_sri;
+ const UCKeyStateTerminators* m_st;
+ UInt16 m_spaceOutput;
+};
diff --git a/src/lib/platform/XWindowsClipboard.cpp b/src/lib/platform/XWindowsClipboard.cpp
new file mode 100644
index 0000000..8e7d864
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboard.cpp
@@ -0,0 +1,1525 @@
+/*
+ * barrier -- 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 "platform/XWindowsClipboard.h"
+
+#include "platform/XWindowsClipboardTextConverter.h"
+#include "platform/XWindowsClipboardUCS2Converter.h"
+#include "platform/XWindowsClipboardUTF8Converter.h"
+#include "platform/XWindowsClipboardHTMLConverter.h"
+#include "platform/XWindowsClipboardBMPConverter.h"
+#include "platform/XWindowsUtil.h"
+#include "mt/Thread.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+#include "base/Stopwatch.h"
+#include "common/stdvector.h"
+
+#include <cstdio>
+#include <cstring>
+#include <X11/Xatom.h>
+
+//
+// XWindowsClipboard
+//
+
+XWindowsClipboard::XWindowsClipboard(Display* display,
+ Window window, ClipboardID id) :
+ m_display(display),
+ m_window(window),
+ m_id(id),
+ m_open(false),
+ m_time(0),
+ m_owner(false),
+ m_timeOwned(0),
+ m_timeLost(0)
+{
+ // get some atoms
+ m_atomTargets = XInternAtom(m_display, "TARGETS", False);
+ m_atomMultiple = XInternAtom(m_display, "MULTIPLE", False);
+ m_atomTimestamp = XInternAtom(m_display, "TIMESTAMP", False);
+ m_atomInteger = XInternAtom(m_display, "INTEGER", False);
+ m_atomAtom = XInternAtom(m_display, "ATOM", False);
+ m_atomAtomPair = XInternAtom(m_display, "ATOM_PAIR", False);
+ m_atomData = XInternAtom(m_display, "CLIP_TEMPORARY", False);
+ m_atomINCR = XInternAtom(m_display, "INCR", False);
+ m_atomMotifClipLock = XInternAtom(m_display, "_MOTIF_CLIP_LOCK", False);
+ m_atomMotifClipHeader = XInternAtom(m_display, "_MOTIF_CLIP_HEADER", False);
+ m_atomMotifClipAccess = XInternAtom(m_display,
+ "_MOTIF_CLIP_LOCK_ACCESS_VALID", False);
+ m_atomGDKSelection = XInternAtom(m_display, "GDK_SELECTION", False);
+
+ // set selection atom based on clipboard id
+ switch (id) {
+ case kClipboardClipboard:
+ m_selection = XInternAtom(m_display, "CLIPBOARD", False);
+ break;
+
+ case kClipboardSelection:
+ default:
+ m_selection = XA_PRIMARY;
+ break;
+ }
+
+ // add converters, most desired first
+ m_converters.push_back(new XWindowsClipboardHTMLConverter(m_display,
+ "text/html"));
+ m_converters.push_back(new XWindowsClipboardBMPConverter(m_display));
+ m_converters.push_back(new XWindowsClipboardUTF8Converter(m_display,
+ "text/plain;charset=UTF-8"));
+ m_converters.push_back(new XWindowsClipboardUTF8Converter(m_display,
+ "UTF8_STRING"));
+ m_converters.push_back(new XWindowsClipboardUCS2Converter(m_display,
+ "text/plain;charset=ISO-10646-UCS-2"));
+ m_converters.push_back(new XWindowsClipboardUCS2Converter(m_display,
+ "text/unicode"));
+ m_converters.push_back(new XWindowsClipboardTextConverter(m_display,
+ "text/plain"));
+ m_converters.push_back(new XWindowsClipboardTextConverter(m_display,
+ "STRING"));
+
+ // we have no data
+ clearCache();
+}
+
+XWindowsClipboard::~XWindowsClipboard()
+{
+ clearReplies();
+ clearConverters();
+}
+
+void
+XWindowsClipboard::lost(Time time)
+{
+ LOG((CLOG_DEBUG "lost clipboard %d ownership at %d", m_id, time));
+ if (m_owner) {
+ m_owner = false;
+ m_timeLost = time;
+ clearCache();
+ }
+}
+
+void
+XWindowsClipboard::addRequest(Window owner, Window requestor,
+ Atom target, ::Time time, Atom property)
+{
+ // must be for our window and we must have owned the selection
+ // at the given time.
+ bool success = false;
+ if (owner == m_window) {
+ LOG((CLOG_DEBUG1 "request for clipboard %d, target %s by 0x%08x (property=%s)", m_selection, XWindowsUtil::atomToString(m_display, target).c_str(), requestor, XWindowsUtil::atomToString(m_display, property).c_str()));
+ if (wasOwnedAtTime(time)) {
+ if (target == m_atomMultiple) {
+ // add a multiple request. property may not be None
+ // according to ICCCM.
+ if (property != None) {
+ success = insertMultipleReply(requestor, time, property);
+ }
+ }
+ else {
+ addSimpleRequest(requestor, target, time, property);
+
+ // addSimpleRequest() will have already handled failure
+ success = true;
+ }
+ }
+ else {
+ LOG((CLOG_DEBUG1 "failed, not owned at time %d", time));
+ }
+ }
+
+ if (!success) {
+ // send failure
+ LOG((CLOG_DEBUG1 "failed"));
+ insertReply(new Reply(requestor, target, time));
+ }
+
+ // send notifications that are pending
+ pushReplies();
+}
+
+bool
+XWindowsClipboard::addSimpleRequest(Window requestor,
+ Atom target, ::Time time, Atom property)
+{
+ // obsolete requestors may supply a None property. in
+ // that case we use the target as the property to store
+ // the conversion.
+ if (property == None) {
+ property = target;
+ }
+
+ // handle targets
+ String data;
+ Atom type = None;
+ int format = 0;
+ if (target == m_atomTargets) {
+ type = getTargetsData(data, &format);
+ }
+ else if (target == m_atomTimestamp) {
+ type = getTimestampData(data, &format);
+ }
+ else {
+ IXWindowsClipboardConverter* converter = getConverter(target);
+ if (converter != NULL) {
+ IClipboard::EFormat clipboardFormat = converter->getFormat();
+ if (m_added[clipboardFormat]) {
+ try {
+ data = converter->fromIClipboard(m_data[clipboardFormat]);
+ format = converter->getDataSize();
+ type = converter->getAtom();
+ }
+ catch (...) {
+ // ignore -- cannot convert
+ }
+ }
+ }
+ }
+
+ if (type != None) {
+ // success
+ LOG((CLOG_DEBUG1 "success"));
+ insertReply(new Reply(requestor, target, time,
+ property, data, type, format));
+ return true;
+ }
+ else {
+ // failure
+ LOG((CLOG_DEBUG1 "failed"));
+ insertReply(new Reply(requestor, target, time));
+ return false;
+ }
+}
+
+bool
+XWindowsClipboard::processRequest(Window requestor,
+ ::Time /*time*/, Atom property)
+{
+ ReplyMap::iterator index = m_replies.find(requestor);
+ if (index == m_replies.end()) {
+ // unknown requestor window
+ return false;
+ }
+ LOG((CLOG_DEBUG1 "received property %s delete from 0x08%x", XWindowsUtil::atomToString(m_display, property).c_str(), requestor));
+
+ // find the property in the known requests. it should be the
+ // first property but we'll check 'em all if we have to.
+ ReplyList& replies = index->second;
+ for (ReplyList::iterator index2 = replies.begin();
+ index2 != replies.end(); ++index2) {
+ Reply* reply = *index2;
+ if (reply->m_replied && reply->m_property == property) {
+ // if reply is complete then remove it and start the
+ // next one.
+ pushReplies(index, replies, index2);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+XWindowsClipboard::destroyRequest(Window requestor)
+{
+ ReplyMap::iterator index = m_replies.find(requestor);
+ if (index == m_replies.end()) {
+ // unknown requestor window
+ return false;
+ }
+
+ // destroy all replies for this window
+ clearReplies(index->second);
+ m_replies.erase(index);
+
+ // note -- we don't stop watching the window for events because
+ // we're called in response to the window being destroyed.
+
+ return true;
+}
+
+Window
+XWindowsClipboard::getWindow() const
+{
+ return m_window;
+}
+
+Atom
+XWindowsClipboard::getSelection() const
+{
+ return m_selection;
+}
+
+bool
+XWindowsClipboard::empty()
+{
+ assert(m_open);
+
+ LOG((CLOG_DEBUG "empty clipboard %d", m_id));
+
+ // assert ownership of clipboard
+ XSetSelectionOwner(m_display, m_selection, m_window, m_time);
+ if (XGetSelectionOwner(m_display, m_selection) != m_window) {
+ LOG((CLOG_DEBUG "failed to grab clipboard %d", m_id));
+ return false;
+ }
+
+ // clear all data. since we own the data now, the cache is up
+ // to date.
+ clearCache();
+ m_cached = true;
+
+ // FIXME -- actually delete motif clipboard items?
+ // FIXME -- do anything to motif clipboard properties?
+
+ // save time
+ m_timeOwned = m_time;
+ m_timeLost = 0;
+
+ // we're the owner now
+ m_owner = true;
+ LOG((CLOG_DEBUG "grabbed clipboard %d", m_id));
+
+ return true;
+}
+
+void
+XWindowsClipboard::add(EFormat format, const String& data)
+{
+ assert(m_open);
+ assert(m_owner);
+
+ LOG((CLOG_DEBUG "add %d bytes to clipboard %d format: %d", data.size(), m_id, format));
+
+ m_data[format] = data;
+ m_added[format] = true;
+
+ // FIXME -- set motif clipboard item?
+}
+
+bool
+XWindowsClipboard::open(Time time) const
+{
+ if (m_open) {
+ LOG((CLOG_DEBUG "failed to open clipboard: already opened"));
+ return false;
+ }
+
+ LOG((CLOG_DEBUG "open clipboard %d", m_id));
+
+ // assume not motif
+ m_motif = false;
+
+ // lock clipboard
+ if (m_id == kClipboardClipboard) {
+ if (!motifLockClipboard()) {
+ return false;
+ }
+
+ // check if motif owns the selection. unlock motif clipboard
+ // if it does not.
+ m_motif = motifOwnsClipboard();
+ LOG((CLOG_DEBUG1 "motif does %sown clipboard", m_motif ? "" : "not "));
+ if (!m_motif) {
+ motifUnlockClipboard();
+ }
+ }
+
+ // now open
+ m_open = true;
+ m_time = time;
+
+ // be sure to flush the cache later if it's dirty
+ m_checkCache = true;
+
+ return true;
+}
+
+void
+XWindowsClipboard::close() const
+{
+ assert(m_open);
+
+ LOG((CLOG_DEBUG "close clipboard %d", m_id));
+
+ // unlock clipboard
+ if (m_motif) {
+ motifUnlockClipboard();
+ }
+
+ m_motif = false;
+ m_open = false;
+}
+
+IClipboard::Time
+XWindowsClipboard::getTime() const
+{
+ checkCache();
+ return m_timeOwned;
+}
+
+bool
+XWindowsClipboard::has(EFormat format) const
+{
+ assert(m_open);
+
+ fillCache();
+ return m_added[format];
+}
+
+String
+XWindowsClipboard::get(EFormat format) const
+{
+ assert(m_open);
+
+ fillCache();
+ return m_data[format];
+}
+
+void
+XWindowsClipboard::clearConverters()
+{
+ for (ConverterList::iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ delete *index;
+ }
+ m_converters.clear();
+}
+
+IXWindowsClipboardConverter*
+XWindowsClipboard::getConverter(Atom target, bool onlyIfNotAdded) const
+{
+ IXWindowsClipboardConverter* converter = NULL;
+ for (ConverterList::const_iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ converter = *index;
+ if (converter->getAtom() == target) {
+ break;
+ }
+ }
+ if (converter == NULL) {
+ LOG((CLOG_DEBUG1 " no converter for target %s", XWindowsUtil::atomToString(m_display, target).c_str()));
+ return NULL;
+ }
+
+ // optionally skip already handled targets
+ if (onlyIfNotAdded) {
+ if (m_added[converter->getFormat()]) {
+ LOG((CLOG_DEBUG1 " skipping handled format %d", converter->getFormat()));
+ return NULL;
+ }
+ }
+
+ return converter;
+}
+
+void
+XWindowsClipboard::checkCache() const
+{
+ if (!m_checkCache) {
+ return;
+ }
+ m_checkCache = false;
+
+ // get the time the clipboard ownership was taken by the current
+ // owner.
+ if (m_motif) {
+ m_timeOwned = motifGetTime();
+ }
+ else {
+ m_timeOwned = icccmGetTime();
+ }
+
+ // if we can't get the time then use the time passed to us
+ if (m_timeOwned == 0) {
+ m_timeOwned = m_time;
+ }
+
+ // if the cache is dirty then flush it
+ if (m_timeOwned != m_cacheTime) {
+ clearCache();
+ }
+}
+
+void
+XWindowsClipboard::clearCache() const
+{
+ const_cast<XWindowsClipboard*>(this)->doClearCache();
+}
+
+void
+XWindowsClipboard::doClearCache()
+{
+ m_checkCache = false;
+ m_cached = false;
+ for (SInt32 index = 0; index < kNumFormats; ++index) {
+ m_data[index] = "";
+ m_added[index] = false;
+ }
+}
+
+void
+XWindowsClipboard::fillCache() const
+{
+ // get the selection data if not already cached
+ checkCache();
+ if (!m_cached) {
+ const_cast<XWindowsClipboard*>(this)->doFillCache();
+ }
+}
+
+void
+XWindowsClipboard::doFillCache()
+{
+ if (m_motif) {
+ motifFillCache();
+ }
+ else {
+ icccmFillCache();
+ }
+ m_checkCache = false;
+ m_cached = true;
+ m_cacheTime = m_timeOwned;
+}
+
+void
+XWindowsClipboard::icccmFillCache()
+{
+ LOG((CLOG_DEBUG "ICCCM fill clipboard %d", m_id));
+
+ // see if we can get the list of available formats from the selection.
+ // if not then use a default list of formats. note that some clipboard
+ // owners are broken and report TARGETS as the type of the TARGETS data
+ // instead of the correct type ATOM; allow either.
+ const Atom atomTargets = m_atomTargets;
+ Atom target;
+ String data;
+ if (!icccmGetSelection(atomTargets, &target, &data) ||
+ (target != m_atomAtom && target != m_atomTargets)) {
+ LOG((CLOG_DEBUG1 "selection doesn't support TARGETS"));
+ data = "";
+ XWindowsUtil::appendAtomData(data, XA_STRING);
+ }
+
+ XWindowsUtil::convertAtomProperty(data);
+ const Atom* targets = reinterpret_cast<const Atom*>(data.data()); // TODO: Safe?
+ const UInt32 numTargets = data.size() / sizeof(Atom);
+ LOG((CLOG_DEBUG " available targets: %s", XWindowsUtil::atomsToString(m_display, targets, numTargets).c_str()));
+
+ // try each converter in order (because they're in order of
+ // preference).
+ for (ConverterList::const_iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ IXWindowsClipboardConverter* converter = *index;
+
+ // skip already handled targets
+ if (m_added[converter->getFormat()]) {
+ continue;
+ }
+
+ // see if atom is in target list
+ Atom target = None;
+ // XXX -- just ask for the converter's target to see if it's
+ // available rather than checking TARGETS. i've seen clipboard
+ // owners that don't report all the targets they support.
+ target = converter->getAtom();
+ /*
+ for (UInt32 i = 0; i < numTargets; ++i) {
+ if (converter->getAtom() == targets[i]) {
+ target = targets[i];
+ break;
+ }
+ }
+ */
+ if (target == None) {
+ continue;
+ }
+
+ // get the data
+ Atom actualTarget;
+ String targetData;
+ if (!icccmGetSelection(target, &actualTarget, &targetData)) {
+ LOG((CLOG_DEBUG1 " no data for target %s", XWindowsUtil::atomToString(m_display, target).c_str()));
+ continue;
+ }
+
+ // add to clipboard and note we've done it
+ IClipboard::EFormat format = converter->getFormat();
+ m_data[format] = converter->toIClipboard(targetData);
+ m_added[format] = true;
+ LOG((CLOG_DEBUG "added format %d for target %s (%u %s)", format, XWindowsUtil::atomToString(m_display, target).c_str(), targetData.size(), targetData.size() == 1 ? "byte" : "bytes"));
+ }
+}
+
+bool
+XWindowsClipboard::icccmGetSelection(Atom target,
+ Atom* actualTarget, String* data) const
+{
+ assert(actualTarget != NULL);
+ assert(data != NULL);
+
+ // request data conversion
+ CICCCMGetClipboard getter(m_window, m_time, m_atomData);
+ if (!getter.readClipboard(m_display, m_selection,
+ target, actualTarget, data)) {
+ LOG((CLOG_DEBUG1 "can't get data for selection target %s", XWindowsUtil::atomToString(m_display, target).c_str()));
+ LOGC(getter.m_error, (CLOG_WARN "ICCCM violation by clipboard owner"));
+ return false;
+ }
+ else if (*actualTarget == None) {
+ LOG((CLOG_DEBUG1 "selection conversion failed for target %s", XWindowsUtil::atomToString(m_display, target).c_str()));
+ return false;
+ }
+ return true;
+}
+
+IClipboard::Time
+XWindowsClipboard::icccmGetTime() const
+{
+ Atom actualTarget;
+ String data;
+ if (icccmGetSelection(m_atomTimestamp, &actualTarget, &data) &&
+ actualTarget == m_atomInteger) {
+ Time time = *reinterpret_cast<const Time*>(data.data());
+ LOG((CLOG_DEBUG1 "got ICCCM time %d", time));
+ return time;
+ }
+ else {
+ // no timestamp
+ LOG((CLOG_DEBUG1 "can't get ICCCM time"));
+ return 0;
+ }
+}
+
+bool
+XWindowsClipboard::motifLockClipboard() const
+{
+ // fail if anybody owns the lock (even us, so this is non-recursive)
+ Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock);
+ if (lockOwner != None) {
+ LOG((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner));
+ return false;
+ }
+
+ // try to grab the lock
+ // FIXME -- is this right? there's a race condition here --
+ // A grabs successfully, B grabs successfully, A thinks it
+ // still has the grab until it gets a SelectionClear.
+ Time time = XWindowsUtil::getCurrentTime(m_display, m_window);
+ XSetSelectionOwner(m_display, m_atomMotifClipLock, m_window, time);
+ lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock);
+ if (lockOwner != m_window) {
+ LOG((CLOG_DEBUG1 "motif lock owner 0x%08x", lockOwner));
+ return false;
+ }
+
+ LOG((CLOG_DEBUG1 "locked motif clipboard"));
+ return true;
+}
+
+void
+XWindowsClipboard::motifUnlockClipboard() const
+{
+ LOG((CLOG_DEBUG1 "unlocked motif clipboard"));
+
+ // fail if we don't own the lock
+ Window lockOwner = XGetSelectionOwner(m_display, m_atomMotifClipLock);
+ if (lockOwner != m_window) {
+ return;
+ }
+
+ // release lock
+ Time time = XWindowsUtil::getCurrentTime(m_display, m_window);
+ XSetSelectionOwner(m_display, m_atomMotifClipLock, None, time);
+}
+
+bool
+XWindowsClipboard::motifOwnsClipboard() const
+{
+ // get the current selection owner
+ // FIXME -- this can't be right. even if the window is destroyed
+ // Motif will still have a valid clipboard. how can we tell if
+ // some other client owns CLIPBOARD?
+ Window owner = XGetSelectionOwner(m_display, m_selection);
+ if (owner == None) {
+ return false;
+ }
+
+ // get the Motif clipboard header property from the root window
+ Atom target;
+ SInt32 format;
+ String data;
+ Window root = RootWindow(m_display, DefaultScreen(m_display));
+ if (!XWindowsUtil::getWindowProperty(m_display, root,
+ m_atomMotifClipHeader,
+ &data, &target, &format, False)) {
+ return false;
+ }
+
+ // check the owner window against the current clipboard owner
+ if (data.size() >= sizeof(MotifClipHeader)) {
+ MotifClipHeader header;
+ std::memcpy (&header, data.data(), sizeof(header));
+ if ((header.m_id == kMotifClipHeader) &&
+ (static_cast<Window>(header.m_selectionOwner) == owner)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+XWindowsClipboard::motifFillCache()
+{
+ LOG((CLOG_DEBUG "Motif fill clipboard %d", m_id));
+
+ // get the Motif clipboard header property from the root window
+ Atom target;
+ SInt32 format;
+ String data;
+ Window root = RootWindow(m_display, DefaultScreen(m_display));
+ if (!XWindowsUtil::getWindowProperty(m_display, root,
+ m_atomMotifClipHeader,
+ &data, &target, &format, False)) {
+ return;
+ }
+
+ MotifClipHeader header;
+ if (data.size() < sizeof(header)) { // check that the header is okay
+ return;
+ }
+ std::memcpy (&header, data.data(), sizeof(header));
+ if (header.m_id != kMotifClipHeader || header.m_numItems < 1) {
+ return;
+ }
+
+ // get the Motif item property from the root window
+ char name[18 + 20];
+ sprintf(name, "_MOTIF_CLIP_ITEM_%d", header.m_item);
+ Atom atomItem = XInternAtom(m_display, name, False);
+ data = "";
+ if (!XWindowsUtil::getWindowProperty(m_display, root,
+ atomItem, &data,
+ &target, &format, False)) {
+ return;
+ }
+
+ MotifClipItem item;
+ if (data.size() < sizeof(item)) { // check that the item is okay
+ return;
+ }
+ std::memcpy (&item, data.data(), sizeof(item));
+ if (item.m_id != kMotifClipItem ||
+ item.m_numFormats - item.m_numDeletedFormats < 1) {
+ return;
+ }
+
+ // format list is after static item structure elements
+ const SInt32 numFormats = item.m_numFormats - item.m_numDeletedFormats;
+ const SInt32* formats = reinterpret_cast<const SInt32*>(item.m_size +
+ static_cast<const char*>(data.data()));
+
+ // get the available formats
+ typedef std::map<Atom, String> MotifFormatMap;
+ MotifFormatMap motifFormats;
+ for (SInt32 i = 0; i < numFormats; ++i) {
+ // get Motif format property from the root window
+ sprintf(name, "_MOTIF_CLIP_ITEM_%d", formats[i]);
+ Atom atomFormat = XInternAtom(m_display, name, False);
+ String data;
+ if (!XWindowsUtil::getWindowProperty(m_display, root,
+ atomFormat, &data,
+ &target, &format, False)) {
+ continue;
+ }
+
+ // check that the format is okay
+ MotifClipFormat motifFormat;
+ if (data.size() < sizeof(motifFormat)) {
+ continue;
+ }
+ std::memcpy (&motifFormat, data.data(), sizeof(motifFormat));
+ if (motifFormat.m_id != kMotifClipFormat ||
+ motifFormat.m_length < 0 ||
+ motifFormat.m_type == None ||
+ motifFormat.m_deleted != 0) {
+ continue;
+ }
+
+ // save it
+ motifFormats.insert(std::make_pair(motifFormat.m_type, data));
+ }
+ //const UInt32 numMotifFormats = motifFormats.size();
+
+ // try each converter in order (because they're in order of
+ // preference).
+ for (ConverterList::const_iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ IXWindowsClipboardConverter* converter = *index;
+
+ // skip already handled targets
+ if (m_added[converter->getFormat()]) {
+ continue;
+ }
+
+ // see if atom is in target list
+ MotifFormatMap::const_iterator index2 =
+ motifFormats.find(converter->getAtom());
+ if (index2 == motifFormats.end()) {
+ continue;
+ }
+
+ // get format
+ MotifClipFormat motifFormat;
+ std::memcpy (&motifFormat, index2->second.data(), sizeof(motifFormat));
+ const Atom target = motifFormat.m_type;
+
+ // get the data (finally)
+ Atom actualTarget;
+ String targetData;
+ if (!motifGetSelection(&motifFormat, &actualTarget, &targetData)) {
+ LOG((CLOG_DEBUG1 " no data for target %s", XWindowsUtil::atomToString(m_display, target).c_str()));
+ continue;
+ }
+
+ // add to clipboard and note we've done it
+ IClipboard::EFormat format = converter->getFormat();
+ m_data[format] = converter->toIClipboard(targetData);
+ m_added[format] = true;
+ LOG((CLOG_DEBUG "added format %d for target %s", format, XWindowsUtil::atomToString(m_display, target).c_str()));
+ }
+}
+
+bool
+XWindowsClipboard::motifGetSelection(const MotifClipFormat* format,
+ Atom* actualTarget, String* data) const
+{
+ // if the current clipboard owner and the owner indicated by the
+ // motif clip header are the same then transfer via a property on
+ // the root window, otherwise transfer as a normal ICCCM client.
+ if (!motifOwnsClipboard()) {
+ return icccmGetSelection(format->m_type, actualTarget, data);
+ }
+
+ // use motif way
+ // FIXME -- this isn't right. it'll only work if the data is
+ // already stored on the root window and only if it fits in a
+ // property. motif has some scheme for transferring part by
+ // part that i don't know.
+ char name[18 + 20];
+ sprintf(name, "_MOTIF_CLIP_ITEM_%d", format->m_data);
+ Atom target = XInternAtom(m_display, name, False);
+ Window root = RootWindow(m_display, DefaultScreen(m_display));
+ return XWindowsUtil::getWindowProperty(m_display, root,
+ target, data,
+ actualTarget, NULL, False);
+}
+
+IClipboard::Time
+XWindowsClipboard::motifGetTime() const
+{
+ return icccmGetTime();
+}
+
+bool
+XWindowsClipboard::insertMultipleReply(Window requestor,
+ ::Time time, Atom property)
+{
+ // get the requested targets
+ Atom target;
+ SInt32 format;
+ String data;
+ if (!XWindowsUtil::getWindowProperty(m_display, requestor,
+ property, &data, &target, &format, False)) {
+ // can't get the requested targets
+ return false;
+ }
+
+ // fail if the requested targets isn't of the correct form
+ if (format != 32 || target != m_atomAtomPair) {
+ return false;
+ }
+
+ // data is a list of atom pairs: target, property
+ XWindowsUtil::convertAtomProperty(data);
+ const Atom* targets = reinterpret_cast<const Atom*>(data.data());
+ const UInt32 numTargets = data.size() / sizeof(Atom);
+
+ // add replies for each target
+ bool changed = false;
+ for (UInt32 i = 0; i < numTargets; i += 2) {
+ const Atom target = targets[i + 0];
+ const Atom property = targets[i + 1];
+ if (!addSimpleRequest(requestor, target, time, property)) {
+ // note that we can't perform the requested conversion
+ XWindowsUtil::replaceAtomData(data, i, None);
+ changed = true;
+ }
+ }
+
+ // update the targets property if we changed it
+ if (changed) {
+ XWindowsUtil::setWindowProperty(m_display, requestor,
+ property, data.data(), data.size(),
+ target, format);
+ }
+
+ // add reply for MULTIPLE request
+ insertReply(new Reply(requestor, m_atomMultiple,
+ time, property, String(), None, 32));
+
+ return true;
+}
+
+void
+XWindowsClipboard::insertReply(Reply* reply)
+{
+ assert(reply != NULL);
+
+ // note -- we must respond to requests in order if requestor,target,time
+ // are the same, otherwise we can use whatever order we like with one
+ // exception: each reply in a MULTIPLE reply must be handled in order
+ // as well. those replies will almost certainly not share targets so
+ // we can't simply use requestor,target,time as map index.
+ //
+ // instead we'll use just the requestor. that's more restrictive than
+ // necessary but we're guaranteed to do things in the right order.
+ // note that we could also include the time in the map index and still
+ // ensure the right order. but since that'll just make it harder to
+ // find the right reply when handling property notify events we stick
+ // to just the requestor.
+
+ const bool newWindow = (m_replies.count(reply->m_requestor) == 0);
+ m_replies[reply->m_requestor].push_back(reply);
+
+ // adjust requestor's event mask if we haven't done so already. we
+ // want events in case the window is destroyed or any of its
+ // properties change.
+ if (newWindow) {
+ // note errors while we adjust event masks
+ bool error = false;
+ {
+ XWindowsUtil::ErrorLock lock(m_display, &error);
+
+ // get and save the current event mask
+ XWindowAttributes attr;
+ XGetWindowAttributes(m_display, reply->m_requestor, &attr);
+ m_eventMasks[reply->m_requestor] = attr.your_event_mask;
+
+ // add the events we want
+ XSelectInput(m_display, reply->m_requestor, attr.your_event_mask |
+ StructureNotifyMask | PropertyChangeMask);
+ }
+
+ // if we failed then the window has already been destroyed
+ if (error) {
+ m_replies.erase(reply->m_requestor);
+ delete reply;
+ }
+ }
+}
+
+void
+XWindowsClipboard::pushReplies()
+{
+ // send the first reply for each window if that reply hasn't
+ // been sent yet.
+ for (ReplyMap::iterator index = m_replies.begin();
+ index != m_replies.end(); ) {
+ assert(!index->second.empty());
+ ReplyList::iterator listit = index->second.begin();
+ while (listit != index->second.end()) {
+ if (!(*listit)->m_replied)
+ break;
+ ++listit;
+ }
+ if (listit != index->second.end() && !(*listit)->m_replied) {
+ pushReplies(index, index->second, listit);
+ }
+ else {
+ ++index;
+ }
+ }
+}
+
+void
+XWindowsClipboard::pushReplies(ReplyMap::iterator& mapIndex,
+ ReplyList& replies, ReplyList::iterator index)
+{
+ Reply* reply = *index;
+ while (sendReply(reply)) {
+ // reply is complete. discard it and send the next reply,
+ // if any.
+ index = replies.erase(index);
+ delete reply;
+ if (index == replies.end()) {
+ break;
+ }
+ reply = *index;
+ }
+
+ // if there are no more replies in the list then remove the list
+ // and stop watching the requestor for events.
+ if (replies.empty()) {
+ XWindowsUtil::ErrorLock lock(m_display);
+ Window requestor = mapIndex->first;
+ XSelectInput(m_display, requestor, m_eventMasks[requestor]);
+ m_replies.erase(mapIndex++);
+ m_eventMasks.erase(requestor);
+ }
+ else {
+ ++mapIndex;
+ }
+}
+
+bool
+XWindowsClipboard::sendReply(Reply* reply)
+{
+ assert(reply != NULL);
+
+ // bail out immediately if reply is done
+ if (reply->m_done) {
+ LOG((CLOG_DEBUG1 "clipboard: finished reply to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property));
+ return true;
+ }
+
+ // start in failed state if property is None
+ bool failed = (reply->m_property == None);
+ if (!failed) {
+ LOG((CLOG_DEBUG1 "clipboard: setting property on 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property));
+
+ // send using INCR if already sending incrementally or if reply
+ // is too large, otherwise just send it.
+ const UInt32 maxRequestSize = 3 * XMaxRequestSize(m_display);
+ const bool useINCR = (reply->m_data.size() > maxRequestSize);
+
+ // send INCR reply if incremental and we haven't replied yet
+ if (useINCR && !reply->m_replied) {
+ UInt32 size = reply->m_data.size();
+ if (!XWindowsUtil::setWindowProperty(m_display,
+ reply->m_requestor, reply->m_property,
+ &size, 4, m_atomINCR, 32)) {
+ failed = true;
+ }
+ }
+
+ // send more INCR reply or entire non-incremental reply
+ else {
+ // how much more data should we send?
+ UInt32 size = reply->m_data.size() - reply->m_ptr;
+ if (size > maxRequestSize)
+ size = maxRequestSize;
+
+ // send it
+ if (!XWindowsUtil::setWindowProperty(m_display,
+ reply->m_requestor, reply->m_property,
+ reply->m_data.data() + reply->m_ptr,
+ size,
+ reply->m_type, reply->m_format)) {
+ failed = true;
+ }
+ else {
+ reply->m_ptr += size;
+
+ // we've finished the reply if we just sent the zero
+ // size incremental chunk or if we're not incremental.
+ reply->m_done = (size == 0 || !useINCR);
+ }
+ }
+ }
+
+ // if we've failed then delete the property and say we're done.
+ // if we haven't replied yet then we can send a failure notify,
+ // otherwise we've failed in the middle of an incremental
+ // transfer; i don't know how to cancel that so i'll just send
+ // the final zero-length property.
+ // FIXME -- how do you gracefully cancel an incremental transfer?
+ if (failed) {
+ LOG((CLOG_DEBUG1 "clipboard: sending failure to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property));
+ reply->m_done = true;
+ if (reply->m_property != None) {
+ XWindowsUtil::ErrorLock lock(m_display);
+ XDeleteProperty(m_display, reply->m_requestor, reply->m_property);
+ }
+
+ if (!reply->m_replied) {
+ sendNotify(reply->m_requestor, m_selection,
+ reply->m_target, None,
+ reply->m_time);
+
+ // don't wait for any reply (because we're not expecting one)
+ return true;
+ }
+ else {
+ static const char dummy = 0;
+ XWindowsUtil::setWindowProperty(m_display,
+ reply->m_requestor, reply->m_property,
+ &dummy,
+ 0,
+ reply->m_type, reply->m_format);
+
+ // wait for delete notify
+ return false;
+ }
+ }
+
+ // send notification if we haven't yet
+ if (!reply->m_replied) {
+ LOG((CLOG_DEBUG1 "clipboard: sending notify to 0x%08x,%d,%d", reply->m_requestor, reply->m_target, reply->m_property));
+ reply->m_replied = true;
+
+ // dump every property on the requestor window to the debug2
+ // log. we've seen what appears to be a bug in lesstif and
+ // knowing the properties may help design a workaround, if
+ // it becomes necessary.
+ if (CLOG->getFilter() >= kDEBUG2) {
+ XWindowsUtil::ErrorLock lock(m_display);
+ int n;
+ Atom* props = XListProperties(m_display, reply->m_requestor, &n);
+ LOG((CLOG_DEBUG2 "properties of 0x%08x:", reply->m_requestor));
+ for (int i = 0; i < n; ++i) {
+ Atom target;
+ String data;
+ char* name = XGetAtomName(m_display, props[i]);
+ if (!XWindowsUtil::getWindowProperty(m_display,
+ reply->m_requestor,
+ props[i], &data, &target, NULL, False)) {
+ LOG((CLOG_DEBUG2 " %s: <can't read property>", name));
+ }
+ else {
+ // if there are any non-ascii characters in string
+ // then print the binary data.
+ static const char* hex = "0123456789abcdef";
+ for (String::size_type j = 0; j < data.size(); ++j) {
+ if (data[j] < 32 || data[j] > 126) {
+ String tmp;
+ tmp.reserve(data.size() * 3);
+ for (j = 0; j < data.size(); ++j) {
+ unsigned char v = (unsigned char)data[j];
+ tmp += hex[v >> 16];
+ tmp += hex[v & 15];
+ tmp += ' ';
+ }
+ data = tmp;
+ break;
+ }
+ }
+ char* type = XGetAtomName(m_display, target);
+ LOG((CLOG_DEBUG2 " %s (%s): %s", name, type, data.c_str()));
+ if (type != NULL) {
+ XFree(type);
+ }
+ }
+ if (name != NULL) {
+ XFree(name);
+ }
+ }
+ if (props != NULL) {
+ XFree(props);
+ }
+ }
+
+ sendNotify(reply->m_requestor, m_selection,
+ reply->m_target, reply->m_property,
+ reply->m_time);
+ }
+
+ // wait for delete notify
+ return false;
+}
+
+void
+XWindowsClipboard::clearReplies()
+{
+ for (ReplyMap::iterator index = m_replies.begin();
+ index != m_replies.end(); ++index) {
+ clearReplies(index->second);
+ }
+ m_replies.clear();
+ m_eventMasks.clear();
+}
+
+void
+XWindowsClipboard::clearReplies(ReplyList& replies)
+{
+ for (ReplyList::iterator index = replies.begin();
+ index != replies.end(); ++index) {
+ delete *index;
+ }
+ replies.clear();
+}
+
+void
+XWindowsClipboard::sendNotify(Window requestor,
+ Atom selection, Atom target, Atom property, Time time)
+{
+ XEvent event;
+ event.xselection.type = SelectionNotify;
+ event.xselection.display = m_display;
+ event.xselection.requestor = requestor;
+ event.xselection.selection = selection;
+ event.xselection.target = target;
+ event.xselection.property = property;
+ event.xselection.time = time;
+ XWindowsUtil::ErrorLock lock(m_display);
+ XSendEvent(m_display, requestor, False, 0, &event);
+}
+
+bool
+XWindowsClipboard::wasOwnedAtTime(::Time time) const
+{
+ // not owned if we've never owned the selection
+ checkCache();
+ if (m_timeOwned == 0) {
+ return false;
+ }
+
+ // if time is CurrentTime then return true if we still own the
+ // selection and false if we do not. else if we still own the
+ // selection then get the current time, otherwise use
+ // m_timeLost as the end time.
+ Time lost = m_timeLost;
+ if (m_timeLost == 0) {
+ if (time == CurrentTime) {
+ return true;
+ }
+ else {
+ lost = XWindowsUtil::getCurrentTime(m_display, m_window);
+ }
+ }
+ else {
+ if (time == CurrentTime) {
+ return false;
+ }
+ }
+
+ // compare time to range
+ Time duration = lost - m_timeOwned;
+ Time when = time - m_timeOwned;
+ return (/*when >= 0 &&*/ when <= duration);
+}
+
+Atom
+XWindowsClipboard::getTargetsData(String& data, int* format) const
+{
+ assert(format != NULL);
+
+ // add standard targets
+ XWindowsUtil::appendAtomData(data, m_atomTargets);
+ XWindowsUtil::appendAtomData(data, m_atomMultiple);
+ XWindowsUtil::appendAtomData(data, m_atomTimestamp);
+
+ // add targets we can convert to
+ for (ConverterList::const_iterator index = m_converters.begin();
+ index != m_converters.end(); ++index) {
+ IXWindowsClipboardConverter* converter = *index;
+
+ // skip formats we don't have
+ if (m_added[converter->getFormat()]) {
+ XWindowsUtil::appendAtomData(data, converter->getAtom());
+ }
+ }
+
+ *format = 32;
+ return m_atomAtom;
+}
+
+Atom
+XWindowsClipboard::getTimestampData(String& data, int* format) const
+{
+ assert(format != NULL);
+
+ checkCache();
+ XWindowsUtil::appendTimeData(data, m_timeOwned);
+ *format = 32;
+ return m_atomInteger;
+}
+
+
+//
+// XWindowsClipboard::CICCCMGetClipboard
+//
+
+XWindowsClipboard::CICCCMGetClipboard::CICCCMGetClipboard(
+ Window requestor, Time time, Atom property) :
+ m_requestor(requestor),
+ m_time(time),
+ m_property(property),
+ m_incr(false),
+ m_failed(false),
+ m_done(false),
+ m_reading(false),
+ m_data(NULL),
+ m_actualTarget(NULL),
+ m_error(false)
+{
+ // do nothing
+}
+
+XWindowsClipboard::CICCCMGetClipboard::~CICCCMGetClipboard()
+{
+ // do nothing
+}
+
+bool
+XWindowsClipboard::CICCCMGetClipboard::readClipboard(Display* display,
+ Atom selection, Atom target, Atom* actualTarget, String* data)
+{
+ assert(actualTarget != NULL);
+ assert(data != NULL);
+
+ LOG((CLOG_DEBUG1 "request selection=%s, target=%s, window=%x", XWindowsUtil::atomToString(display, selection).c_str(), XWindowsUtil::atomToString(display, target).c_str(), m_requestor));
+
+ m_atomNone = XInternAtom(display, "NONE", False);
+ m_atomIncr = XInternAtom(display, "INCR", False);
+
+ // save output pointers
+ m_actualTarget = actualTarget;
+ m_data = data;
+
+ // assume failure
+ *m_actualTarget = None;
+ *m_data = "";
+
+ // delete target property
+ XDeleteProperty(display, m_requestor, m_property);
+
+ // select window for property changes
+ XWindowAttributes attr;
+ XGetWindowAttributes(display, m_requestor, &attr);
+ XSelectInput(display, m_requestor,
+ attr.your_event_mask | PropertyChangeMask);
+
+ // request data conversion
+ XConvertSelection(display, selection, target,
+ m_property, m_requestor, m_time);
+
+ // synchronize with server before we start following timeout countdown
+ XSync(display, False);
+
+ // Xlib inexplicably omits the ability to wait for an event with
+ // a timeout. (it's inexplicable because there's no portable way
+ // to do it.) we'll poll until we have what we're looking for or
+ // a timeout expires. we use a timeout so we don't get locked up
+ // by badly behaved selection owners.
+ XEvent xevent;
+ std::vector<XEvent> events;
+ Stopwatch timeout(false); // timer not stopped, not triggered
+ static const double s_timeout = 0.25; // FIXME -- is this too short?
+ bool noWait = false;
+ while (!m_done && !m_failed) {
+ // fail if timeout has expired
+ if (timeout.getTime() >= s_timeout) {
+ m_failed = true;
+ break;
+ }
+
+ // process events if any otherwise sleep
+ if (noWait || XPending(display) > 0) {
+ while (!m_done && !m_failed && (noWait || XPending(display) > 0)) {
+ XNextEvent(display, &xevent);
+ if (!processEvent(display, &xevent)) {
+ // not processed so save it
+ events.push_back(xevent);
+ }
+ else {
+ // reset timer since we've made some progress
+ timeout.reset();
+
+ // don't sleep anymore, just block waiting for events.
+ // we're assuming here that the clipboard owner will
+ // complete the protocol correctly. if we continue to
+ // sleep we'll get very bad performance.
+ noWait = true;
+ }
+ }
+ }
+ else {
+ ARCH->sleep(0.01);
+ }
+ }
+
+ // put unprocessed events back
+ for (UInt32 i = events.size(); i > 0; --i) {
+ XPutBackEvent(display, &events[i - 1]);
+ }
+
+ // restore mask
+ XSelectInput(display, m_requestor, attr.your_event_mask);
+
+ // return success or failure
+ LOG((CLOG_DEBUG1 "request %s after %fs", m_failed ? "failed" : "succeeded", timeout.getTime()));
+ return !m_failed;
+}
+
+bool
+XWindowsClipboard::CICCCMGetClipboard::processEvent(
+ Display* display, XEvent* xevent)
+{
+ // process event
+ switch (xevent->type) {
+ case DestroyNotify:
+ if (xevent->xdestroywindow.window == m_requestor) {
+ m_failed = true;
+ return true;
+ }
+
+ // not interested
+ return false;
+
+ case SelectionNotify:
+ if (xevent->xselection.requestor == m_requestor) {
+ // done if we can't convert
+ if (xevent->xselection.property == None ||
+ xevent->xselection.property == m_atomNone) {
+ m_done = true;
+ return true;
+ }
+
+ // proceed if conversion successful
+ else if (xevent->xselection.property == m_property) {
+ m_reading = true;
+ break;
+ }
+ }
+
+ // otherwise not interested
+ return false;
+
+ case PropertyNotify:
+ // proceed if conversion successful and we're receiving more data
+ if (xevent->xproperty.window == m_requestor &&
+ xevent->xproperty.atom == m_property &&
+ xevent->xproperty.state == PropertyNewValue) {
+ if (!m_reading) {
+ // we haven't gotten the SelectionNotify yet
+ return true;
+ }
+ break;
+ }
+
+ // otherwise not interested
+ return false;
+
+ default:
+ // not interested
+ return false;
+ }
+
+ // get the data from the property
+ Atom target;
+ const String::size_type oldSize = m_data->size();
+ if (!XWindowsUtil::getWindowProperty(display, m_requestor,
+ m_property, m_data, &target, NULL, True)) {
+ // unable to read property
+ m_failed = true;
+ return true;
+ }
+
+ // note if incremental. if we're already incremental then the
+ // selection owner is busted. if the INCR property has no size
+ // then the selection owner is busted.
+ if (target == m_atomIncr) {
+ if (m_incr) {
+ m_failed = true;
+ m_error = true;
+ }
+ else if (m_data->size() == oldSize) {
+ m_failed = true;
+ m_error = true;
+ }
+ else {
+ m_incr = true;
+
+ // discard INCR data
+ *m_data = "";
+ }
+ }
+
+ // handle incremental chunks
+ else if (m_incr) {
+ // if first incremental chunk then save target
+ if (oldSize == 0) {
+ LOG((CLOG_DEBUG1 " INCR first chunk, target %s", XWindowsUtil::atomToString(display, target).c_str()));
+ *m_actualTarget = target;
+ }
+
+ // secondary chunks must have the same target
+ else {
+ if (target != *m_actualTarget) {
+ LOG((CLOG_WARN " INCR target mismatch"));
+ m_failed = true;
+ m_error = true;
+ }
+ }
+
+ // note if this is the final chunk
+ if (m_data->size() == oldSize) {
+ LOG((CLOG_DEBUG1 " INCR final chunk: %d bytes total", m_data->size()));
+ m_done = true;
+ }
+ }
+
+ // not incremental; save the target.
+ else {
+ LOG((CLOG_DEBUG1 " target %s", XWindowsUtil::atomToString(display, target).c_str()));
+ *m_actualTarget = target;
+ m_done = true;
+ }
+
+ // this event has been processed
+ LOGC(!m_incr, (CLOG_DEBUG1 " got data, %d bytes", m_data->size()));
+ return true;
+}
+
+
+//
+// XWindowsClipboard::Reply
+//
+
+XWindowsClipboard::Reply::Reply(Window requestor, Atom target, ::Time time) :
+ m_requestor(requestor),
+ m_target(target),
+ m_time(time),
+ m_property(None),
+ m_replied(false),
+ m_done(false),
+ m_data(),
+ m_type(None),
+ m_format(32),
+ m_ptr(0)
+{
+ // do nothing
+}
+
+XWindowsClipboard::Reply::Reply(Window requestor, Atom target, ::Time time,
+ Atom property, const String& data, Atom type, int format) :
+ m_requestor(requestor),
+ m_target(target),
+ m_time(time),
+ m_property(property),
+ m_replied(false),
+ m_done(false),
+ m_data(data),
+ m_type(type),
+ m_format(format),
+ m_ptr(0)
+{
+ // do nothing
+}
diff --git a/src/lib/platform/XWindowsClipboard.h b/src/lib/platform/XWindowsClipboard.h
new file mode 100644
index 0000000..cf20e82
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboard.h
@@ -0,0 +1,378 @@
+/*
+ * barrier -- 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 "barrier/clipboard_types.h"
+#include "barrier/IClipboard.h"
+#include "common/stdmap.h"
+#include "common/stdlist.h"
+#include "common/stdvector.h"
+
+#if X_DISPLAY_MISSING
+# error X11 is required to build barrier
+#else
+# include <X11/Xlib.h>
+#endif
+
+class IXWindowsClipboardConverter;
+
+//! X11 clipboard implementation
+class XWindowsClipboard : public IClipboard {
+public:
+ /*!
+ Use \c window as the window that owns or interacts with the
+ clipboard identified by \c id.
+ */
+ XWindowsClipboard(Display*, Window window, ClipboardID id);
+ virtual ~XWindowsClipboard();
+
+ //! Notify clipboard was lost
+ /*!
+ Tells clipboard it lost ownership at the given time.
+ */
+ void lost(Time);
+
+ //! Add clipboard request
+ /*!
+ Adds a selection request to the request list. If the given
+ owner window isn't this clipboard's window then this simply
+ sends a failure event to the requestor.
+ */
+ void addRequest(Window owner,
+ Window requestor, Atom target,
+ ::Time time, Atom property);
+
+ //! Process clipboard request
+ /*!
+ Continues processing a selection request. Returns true if the
+ request was handled, false if the request was unknown.
+ */
+ bool processRequest(Window requestor,
+ ::Time time, Atom property);
+
+ //! Cancel clipboard request
+ /*!
+ Terminate a selection request. Returns true iff the request
+ was known and handled.
+ */
+ bool destroyRequest(Window requestor);
+
+ //! Get window
+ /*!
+ Returns the clipboard's window (passed the c'tor).
+ */
+ Window getWindow() const;
+
+ //! Get selection atom
+ /*!
+ Returns the selection atom that identifies the clipboard to X11
+ (e.g. XA_PRIMARY).
+ */
+ Atom getSelection() const;
+
+ // IClipboard overrides
+ virtual bool empty();
+ virtual void add(EFormat, const String& data);
+ virtual bool open(Time) const;
+ virtual void close() const;
+ virtual Time getTime() const;
+ virtual bool has(EFormat) const;
+ virtual String get(EFormat) const;
+
+private:
+ // remove all converters from our list
+ void clearConverters();
+
+ // get the converter for a clipboard format. returns NULL if no
+ // suitable converter. iff onlyIfNotAdded is true then also
+ // return NULL if a suitable converter was found but we already
+ // have data of the converter's clipboard format.
+ IXWindowsClipboardConverter*
+ getConverter(Atom target,
+ bool onlyIfNotAdded = false) const;
+
+ // convert target atom to clipboard format
+ EFormat getFormat(Atom target) const;
+
+ // add a non-MULTIPLE request. does not verify that the selection
+ // was owned at the given time. returns true if the conversion
+ // could be performed, false otherwise. in either case, the
+ // reply is inserted.
+ bool addSimpleRequest(
+ Window requestor, Atom target,
+ ::Time time, Atom property);
+
+ // if not already checked then see if the cache is stale and, if so,
+ // clear it. this has the side effect of updating m_timeOwned.
+ void checkCache() const;
+
+ // clear the cache, resetting the cached flag and the added flag for
+ // each format.
+ void clearCache() const;
+ void doClearCache();
+
+ // cache all formats of the selection
+ void fillCache() const;
+ void doFillCache();
+
+ //
+ // helper classes
+ //
+
+ // read an ICCCM conforming selection
+ class CICCCMGetClipboard {
+ public:
+ CICCCMGetClipboard(Window requestor, Time time, Atom property);
+ ~CICCCMGetClipboard();
+
+ // convert the given selection to the given type. returns
+ // true iff the conversion was successful or the conversion
+ // cannot be performed (in which case *actualTarget == None).
+ bool readClipboard(Display* display,
+ Atom selection, Atom target,
+ Atom* actualTarget, String* data);
+
+ private:
+ bool processEvent(Display* display, XEvent* event);
+
+ private:
+ Window m_requestor;
+ Time m_time;
+ Atom m_property;
+ bool m_incr;
+ bool m_failed;
+ bool m_done;
+
+ // atoms needed for the protocol
+ Atom m_atomNone; // NONE, not None
+ Atom m_atomIncr;
+
+ // true iff we've received the selection notify
+ bool m_reading;
+
+ // the converted selection data
+ String* m_data;
+
+ // the actual type of the data. if this is None then the
+ // selection owner cannot convert to the requested type.
+ Atom* m_actualTarget;
+
+ public:
+ // true iff the selection owner didn't follow ICCCM conventions
+ bool m_error;
+ };
+
+ // Motif structure IDs
+ enum { kMotifClipFormat = 1, kMotifClipItem, kMotifClipHeader };
+
+ // _MOTIF_CLIP_HEADER structure
+ class MotifClipHeader {
+ public:
+ SInt32 m_id; // kMotifClipHeader
+ SInt32 m_pad1[3];
+ SInt32 m_item;
+ SInt32 m_pad2[4];
+ SInt32 m_numItems;
+ SInt32 m_pad3[3];
+ SInt32 m_selectionOwner; // a Window
+ SInt32 m_pad4[2];
+ };
+
+ // Motif clip item structure
+ class MotifClipItem {
+ public:
+ SInt32 m_id; // kMotifClipItem
+ SInt32 m_pad1[5];
+ SInt32 m_size;
+ SInt32 m_numFormats;
+ SInt32 m_numDeletedFormats;
+ SInt32 m_pad2[6];
+ };
+
+ // Motif clip format structure
+ class MotifClipFormat {
+ public:
+ SInt32 m_id; // kMotifClipFormat
+ SInt32 m_pad1[6];
+ SInt32 m_length;
+ SInt32 m_data;
+ SInt32 m_type; // an Atom
+ SInt32 m_pad2[1];
+ SInt32 m_deleted;
+ SInt32 m_pad3[4];
+ };
+
+ // stores data needed to respond to a selection request
+ class Reply {
+ public:
+ Reply(Window, Atom target, ::Time);
+ Reply(Window, Atom target, ::Time, Atom property,
+ const String& data, Atom type, int format);
+
+ public:
+ // information about the request
+ Window m_requestor;
+ Atom m_target;
+ ::Time m_time;
+ Atom m_property;
+
+ // true iff we've sent the notification for this reply
+ bool m_replied;
+
+ // true iff the reply has sent its last message
+ bool m_done;
+
+ // the data to send and its type and format
+ String m_data;
+ Atom m_type;
+ int m_format;
+
+ // index of next byte in m_data to send
+ UInt32 m_ptr;
+ };
+ typedef std::list<Reply*> ReplyList;
+ typedef std::map<Window, ReplyList> ReplyMap;
+ typedef std::map<Window, long> ReplyEventMask;
+
+ // ICCCM interoperability methods
+ void icccmFillCache();
+ bool icccmGetSelection(Atom target,
+ Atom* actualTarget, String* data) const;
+ Time icccmGetTime() const;
+
+ // motif interoperability methods
+ bool motifLockClipboard() const;
+ void motifUnlockClipboard() const;
+ bool motifOwnsClipboard() const;
+ void motifFillCache();
+ bool motifGetSelection(const MotifClipFormat*,
+ Atom* actualTarget, String* data) const;
+ Time motifGetTime() const;
+
+ // reply methods
+ bool insertMultipleReply(Window, ::Time, Atom);
+ void insertReply(Reply*);
+ void pushReplies();
+ void pushReplies(ReplyMap::iterator&,
+ ReplyList&, ReplyList::iterator);
+ bool sendReply(Reply*);
+ void clearReplies();
+ void clearReplies(ReplyList&);
+ void sendNotify(Window requestor, Atom selection,
+ Atom target, Atom property, Time time);
+ bool wasOwnedAtTime(::Time) const;
+
+ // data conversion methods
+ Atom getTargetsData(String&, int* format) const;
+ Atom getTimestampData(String&, int* format) const;
+
+private:
+ typedef std::vector<IXWindowsClipboardConverter*> ConverterList;
+
+ Display* m_display;
+ Window m_window;
+ ClipboardID m_id;
+ Atom m_selection;
+ mutable bool m_open;
+ mutable Time m_time;
+ bool m_owner;
+ mutable Time m_timeOwned;
+ Time m_timeLost;
+
+ // true iff open and clipboard owned by a motif app
+ mutable bool m_motif;
+
+ // the added/cached clipboard data
+ mutable bool m_checkCache;
+ bool m_cached;
+ Time m_cacheTime;
+ bool m_added[kNumFormats];
+ String m_data[kNumFormats];
+
+ // conversion request replies
+ ReplyMap m_replies;
+ ReplyEventMask m_eventMasks;
+
+ // clipboard format converters
+ ConverterList m_converters;
+
+ // atoms we'll need
+ Atom m_atomTargets;
+ Atom m_atomMultiple;
+ Atom m_atomTimestamp;
+ Atom m_atomInteger;
+ Atom m_atomAtom;
+ Atom m_atomAtomPair;
+ Atom m_atomData;
+ Atom m_atomINCR;
+ Atom m_atomMotifClipLock;
+ Atom m_atomMotifClipHeader;
+ Atom m_atomMotifClipAccess;
+ Atom m_atomGDKSelection;
+};
+
+//! Clipboard format converter interface
+/*!
+This interface defines the methods common to all X11 clipboard format
+converters.
+*/
+class IXWindowsClipboardConverter : public IInterface {
+public:
+ //! @name accessors
+ //@{
+
+ //! Get clipboard format
+ /*!
+ Return the clipboard format this object converts from/to.
+ */
+ virtual IClipboard::EFormat
+ getFormat() const = 0;
+
+ //! Get X11 format atom
+ /*!
+ Return the atom representing the X selection format that
+ this object converts from/to.
+ */
+ virtual Atom getAtom() const = 0;
+
+ //! Get X11 property datum size
+ /*!
+ Return the size (in bits) of data elements returned by
+ toIClipboard().
+ */
+ virtual int getDataSize() const = 0;
+
+ //! Convert from IClipboard format
+ /*!
+ Convert from the IClipboard format to the X selection format.
+ The input data must be in the IClipboard format returned by
+ getFormat(). The return data will be in the X selection
+ format returned by getAtom().
+ */
+ virtual String fromIClipboard(const String&) const = 0;
+
+ //! Convert to IClipboard format
+ /*!
+ Convert from the X selection format to the IClipboard format
+ (i.e., the reverse of fromIClipboard()).
+ */
+ virtual String toIClipboard(const String&) const = 0;
+
+ //@}
+};
diff --git a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp
new file mode 100644
index 0000000..493b1e8
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.cpp
@@ -0,0 +1,191 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/XWindowsClipboardAnyBitmapConverter.h"
+
+// BMP info header structure
+struct CBMPInfoHeader {
+public:
+ UInt32 biSize;
+ SInt32 biWidth;
+ SInt32 biHeight;
+ UInt16 biPlanes;
+ UInt16 biBitCount;
+ UInt32 biCompression;
+ UInt32 biSizeImage;
+ SInt32 biXPelsPerMeter;
+ SInt32 biYPelsPerMeter;
+ UInt32 biClrUsed;
+ UInt32 biClrImportant;
+};
+
+// BMP is little-endian
+
+static
+void
+toLE(UInt8*& dst, UInt16 src)
+{
+ dst[0] = static_cast<UInt8>(src & 0xffu);
+ dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
+ dst += 2;
+}
+
+static
+void
+toLE(UInt8*& dst, SInt32 src)
+{
+ dst[0] = static_cast<UInt8>(src & 0xffu);
+ dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
+ dst[2] = static_cast<UInt8>((src >> 16) & 0xffu);
+ dst[3] = static_cast<UInt8>((src >> 24) & 0xffu);
+ dst += 4;
+}
+
+static
+void
+toLE(UInt8*& dst, UInt32 src)
+{
+ dst[0] = static_cast<UInt8>(src & 0xffu);
+ dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
+ dst[2] = static_cast<UInt8>((src >> 16) & 0xffu);
+ dst[3] = static_cast<UInt8>((src >> 24) & 0xffu);
+ dst += 4;
+}
+
+static inline
+UInt16
+fromLEU16(const UInt8* data)
+{
+ return static_cast<UInt16>(data[0]) |
+ (static_cast<UInt16>(data[1]) << 8);
+}
+
+static inline
+SInt32
+fromLES32(const UInt8* data)
+{
+ return static_cast<SInt32>(static_cast<UInt32>(data[0]) |
+ (static_cast<UInt32>(data[1]) << 8) |
+ (static_cast<UInt32>(data[2]) << 16) |
+ (static_cast<UInt32>(data[3]) << 24));
+}
+
+static inline
+UInt32
+fromLEU32(const UInt8* data)
+{
+ return static_cast<UInt32>(data[0]) |
+ (static_cast<UInt32>(data[1]) << 8) |
+ (static_cast<UInt32>(data[2]) << 16) |
+ (static_cast<UInt32>(data[3]) << 24);
+}
+
+
+//
+// XWindowsClipboardAnyBitmapConverter
+//
+
+XWindowsClipboardAnyBitmapConverter::XWindowsClipboardAnyBitmapConverter()
+{
+ // do nothing
+}
+
+XWindowsClipboardAnyBitmapConverter::~XWindowsClipboardAnyBitmapConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+XWindowsClipboardAnyBitmapConverter::getFormat() const
+{
+ return IClipboard::kBitmap;
+}
+
+int
+XWindowsClipboardAnyBitmapConverter::getDataSize() const
+{
+ return 8;
+}
+
+String
+XWindowsClipboardAnyBitmapConverter::fromIClipboard(const String& bmp) const
+{
+ // fill BMP info header with native-endian data
+ CBMPInfoHeader infoHeader;
+ const UInt8* rawBMPInfoHeader = reinterpret_cast<const UInt8*>(bmp.data());
+ infoHeader.biSize = fromLEU32(rawBMPInfoHeader + 0);
+ infoHeader.biWidth = fromLES32(rawBMPInfoHeader + 4);
+ infoHeader.biHeight = fromLES32(rawBMPInfoHeader + 8);
+ infoHeader.biPlanes = fromLEU16(rawBMPInfoHeader + 12);
+ infoHeader.biBitCount = fromLEU16(rawBMPInfoHeader + 14);
+ infoHeader.biCompression = fromLEU32(rawBMPInfoHeader + 16);
+ infoHeader.biSizeImage = fromLEU32(rawBMPInfoHeader + 20);
+ infoHeader.biXPelsPerMeter = fromLES32(rawBMPInfoHeader + 24);
+ infoHeader.biYPelsPerMeter = fromLES32(rawBMPInfoHeader + 28);
+ infoHeader.biClrUsed = fromLEU32(rawBMPInfoHeader + 32);
+ infoHeader.biClrImportant = fromLEU32(rawBMPInfoHeader + 36);
+
+ // check that format is acceptable
+ if (infoHeader.biSize != 40 ||
+ infoHeader.biWidth == 0 || infoHeader.biHeight == 0 ||
+ infoHeader.biPlanes != 0 || infoHeader.biCompression != 0 ||
+ (infoHeader.biBitCount != 24 && infoHeader.biBitCount != 32)) {
+ return String();
+ }
+
+ // convert to image format
+ const UInt8* rawBMPPixels = rawBMPInfoHeader + 40;
+ if (infoHeader.biBitCount == 24) {
+ return doBGRFromIClipboard(rawBMPPixels,
+ infoHeader.biWidth, infoHeader.biHeight);
+ }
+ else {
+ return doBGRAFromIClipboard(rawBMPPixels,
+ infoHeader.biWidth, infoHeader.biHeight);
+ }
+}
+
+String
+XWindowsClipboardAnyBitmapConverter::toIClipboard(const String& image) const
+{
+ // convert to raw BMP data
+ UInt32 w, h, depth;
+ String rawBMP = doToIClipboard(image, w, h, depth);
+ if (rawBMP.empty() || w == 0 || h == 0 || (depth != 24 && depth != 32)) {
+ return String();
+ }
+
+ // fill BMP info header with little-endian data
+ UInt8 infoHeader[40];
+ UInt8* dst = infoHeader;
+ toLE(dst, static_cast<UInt32>(40));
+ toLE(dst, static_cast<SInt32>(w));
+ toLE(dst, static_cast<SInt32>(h));
+ toLE(dst, static_cast<UInt16>(1));
+ toLE(dst, static_cast<UInt16>(depth));
+ toLE(dst, static_cast<UInt32>(0)); // BI_RGB
+ toLE(dst, static_cast<UInt32>(image.size()));
+ toLE(dst, static_cast<SInt32>(2834)); // 72 dpi
+ toLE(dst, static_cast<SInt32>(2834)); // 72 dpi
+ toLE(dst, static_cast<UInt32>(0));
+ toLE(dst, static_cast<UInt32>(0));
+
+ // construct image
+ return String(reinterpret_cast<const char*>(infoHeader),
+ sizeof(infoHeader)) + rawBMP;
+}
diff --git a/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h
new file mode 100644
index 0000000..d723a33
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardAnyBitmapConverter.h
@@ -0,0 +1,60 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/XWindowsClipboard.h"
+
+//! Convert to/from some text encoding
+class XWindowsClipboardAnyBitmapConverter :
+ public IXWindowsClipboardConverter {
+public:
+ XWindowsClipboardAnyBitmapConverter();
+ virtual ~XWindowsClipboardAnyBitmapConverter();
+
+ // IXWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual Atom getAtom() const = 0;
+ virtual int getDataSize() const;
+ virtual String fromIClipboard(const String&) const;
+ virtual String toIClipboard(const String&) const;
+
+protected:
+ //! Convert from IClipboard format
+ /*!
+ Convert raw BGR pixel data to another image format.
+ */
+ virtual String doBGRFromIClipboard(const UInt8* bgrData,
+ UInt32 w, UInt32 h) const = 0;
+
+ //! Convert from IClipboard format
+ /*!
+ Convert raw BGRA pixel data to another image format.
+ */
+ virtual String doBGRAFromIClipboard(const UInt8* bgrData,
+ UInt32 w, UInt32 h) const = 0;
+
+ //! Convert to IClipboard format
+ /*!
+ Convert an image into raw BGR or BGRA image data and store the
+ width, height, and image depth (24 or 32).
+ */
+ virtual String doToIClipboard(const String&,
+ UInt32& w, UInt32& h, UInt32& depth) const = 0;
+};
diff --git a/src/lib/platform/XWindowsClipboardBMPConverter.cpp b/src/lib/platform/XWindowsClipboardBMPConverter.cpp
new file mode 100644
index 0000000..b4def5b
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardBMPConverter.cpp
@@ -0,0 +1,143 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/XWindowsClipboardBMPConverter.h"
+
+// BMP file header structure
+struct CBMPHeader {
+public:
+ UInt16 type;
+ UInt32 size;
+ UInt16 reserved1;
+ UInt16 reserved2;
+ UInt32 offset;
+};
+
+// BMP is little-endian
+static inline
+UInt32
+fromLEU32(const UInt8* data)
+{
+ return static_cast<UInt32>(data[0]) |
+ (static_cast<UInt32>(data[1]) << 8) |
+ (static_cast<UInt32>(data[2]) << 16) |
+ (static_cast<UInt32>(data[3]) << 24);
+}
+
+static
+void
+toLE(UInt8*& dst, char src)
+{
+ dst[0] = static_cast<UInt8>(src);
+ dst += 1;
+}
+
+static
+void
+toLE(UInt8*& dst, UInt16 src)
+{
+ dst[0] = static_cast<UInt8>(src & 0xffu);
+ dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
+ dst += 2;
+}
+
+static
+void
+toLE(UInt8*& dst, UInt32 src)
+{
+ dst[0] = static_cast<UInt8>(src & 0xffu);
+ dst[1] = static_cast<UInt8>((src >> 8) & 0xffu);
+ dst[2] = static_cast<UInt8>((src >> 16) & 0xffu);
+ dst[3] = static_cast<UInt8>((src >> 24) & 0xffu);
+ dst += 4;
+}
+
+//
+// XWindowsClipboardBMPConverter
+//
+
+XWindowsClipboardBMPConverter::XWindowsClipboardBMPConverter(
+ Display* display) :
+ m_atom(XInternAtom(display, "image/bmp", False))
+{
+ // do nothing
+}
+
+XWindowsClipboardBMPConverter::~XWindowsClipboardBMPConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+XWindowsClipboardBMPConverter::getFormat() const
+{
+ return IClipboard::kBitmap;
+}
+
+Atom
+XWindowsClipboardBMPConverter::getAtom() const
+{
+ return m_atom;
+}
+
+int
+XWindowsClipboardBMPConverter::getDataSize() const
+{
+ return 8;
+}
+
+String
+XWindowsClipboardBMPConverter::fromIClipboard(const String& bmp) const
+{
+ // create BMP image
+ UInt8 header[14];
+ UInt8* dst = header;
+ toLE(dst, 'B');
+ toLE(dst, 'M');
+ toLE(dst, static_cast<UInt32>(14 + bmp.size()));
+ toLE(dst, static_cast<UInt16>(0));
+ toLE(dst, static_cast<UInt16>(0));
+ toLE(dst, static_cast<UInt32>(14 + 40));
+ return String(reinterpret_cast<const char*>(header), 14) + bmp;
+}
+
+String
+XWindowsClipboardBMPConverter::toIClipboard(const String& bmp) const
+{
+ // make sure data is big enough for a BMP file
+ if (bmp.size() <= 14 + 40) {
+ return String();
+ }
+
+ // check BMP file header
+ const UInt8* rawBMPHeader = reinterpret_cast<const UInt8*>(bmp.data());
+ if (rawBMPHeader[0] != 'B' || rawBMPHeader[1] != 'M') {
+ return String();
+ }
+
+ // get offset to image data
+ UInt32 offset = fromLEU32(rawBMPHeader + 10);
+
+ // construct BMP
+ if (offset == 14 + 40) {
+ return bmp.substr(14);
+ }
+ else {
+ return bmp.substr(14, 40) + bmp.substr(offset, bmp.size() - offset);
+ }
+}
diff --git a/src/lib/platform/XWindowsClipboardBMPConverter.h b/src/lib/platform/XWindowsClipboardBMPConverter.h
new file mode 100644
index 0000000..d7813a0
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardBMPConverter.h
@@ -0,0 +1,40 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/XWindowsClipboard.h"
+
+//! Convert to/from some text encoding
+class XWindowsClipboardBMPConverter :
+ public IXWindowsClipboardConverter {
+public:
+ XWindowsClipboardBMPConverter(Display* display);
+ virtual ~XWindowsClipboardBMPConverter();
+
+ // IXWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual Atom getAtom() const;
+ virtual int getDataSize() const;
+ virtual String fromIClipboard(const String&) const;
+ virtual String toIClipboard(const String&) const;
+
+private:
+ Atom m_atom;
+};
diff --git a/src/lib/platform/XWindowsClipboardHTMLConverter.cpp b/src/lib/platform/XWindowsClipboardHTMLConverter.cpp
new file mode 100644
index 0000000..32db724
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardHTMLConverter.cpp
@@ -0,0 +1,67 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/XWindowsClipboardHTMLConverter.h"
+
+#include "base/Unicode.h"
+
+//
+// XWindowsClipboardHTMLConverter
+//
+
+XWindowsClipboardHTMLConverter::XWindowsClipboardHTMLConverter(
+ Display* display, const char* name) :
+ m_atom(XInternAtom(display, name, False))
+{
+ // do nothing
+}
+
+XWindowsClipboardHTMLConverter::~XWindowsClipboardHTMLConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+XWindowsClipboardHTMLConverter::getFormat() const
+{
+ return IClipboard::kHTML;
+}
+
+Atom
+XWindowsClipboardHTMLConverter::getAtom() const
+{
+ return m_atom;
+}
+
+int
+XWindowsClipboardHTMLConverter::getDataSize() const
+{
+ return 8;
+}
+
+String
+XWindowsClipboardHTMLConverter::fromIClipboard(const String& data) const
+{
+ return Unicode::UTF8ToUTF16(data);
+}
+
+String
+XWindowsClipboardHTMLConverter::toIClipboard(const String& data) const
+{
+ return Unicode::UTF16ToUTF8(data);
+}
diff --git a/src/lib/platform/XWindowsClipboardHTMLConverter.h b/src/lib/platform/XWindowsClipboardHTMLConverter.h
new file mode 100644
index 0000000..013aa99
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardHTMLConverter.h
@@ -0,0 +1,42 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/XWindowsClipboard.h"
+
+//! Convert to/from HTML encoding
+class XWindowsClipboardHTMLConverter : public IXWindowsClipboardConverter {
+public:
+ /*!
+ \c name is converted to an atom and that is reported by getAtom().
+ */
+ XWindowsClipboardHTMLConverter(Display* display, const char* name);
+ virtual ~XWindowsClipboardHTMLConverter();
+
+ // IXWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual Atom getAtom() const;
+ virtual int getDataSize() const;
+ virtual String fromIClipboard(const String&) const;
+ virtual String toIClipboard(const String&) const;
+
+private:
+ Atom m_atom;
+};
diff --git a/src/lib/platform/XWindowsClipboardTextConverter.cpp b/src/lib/platform/XWindowsClipboardTextConverter.cpp
new file mode 100644
index 0000000..71b7a84
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardTextConverter.cpp
@@ -0,0 +1,79 @@
+/*
+ * barrier -- 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 "platform/XWindowsClipboardTextConverter.h"
+
+#include "base/Unicode.h"
+
+//
+// XWindowsClipboardTextConverter
+//
+
+XWindowsClipboardTextConverter::XWindowsClipboardTextConverter(
+ Display* display, const char* name) :
+ m_atom(XInternAtom(display, name, False))
+{
+ // do nothing
+}
+
+XWindowsClipboardTextConverter::~XWindowsClipboardTextConverter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+XWindowsClipboardTextConverter::getFormat() const
+{
+ return IClipboard::kText;
+}
+
+Atom
+XWindowsClipboardTextConverter::getAtom() const
+{
+ return m_atom;
+}
+
+int
+XWindowsClipboardTextConverter::getDataSize() const
+{
+ return 8;
+}
+
+String
+XWindowsClipboardTextConverter::fromIClipboard(const String& data) const
+{
+ return Unicode::UTF8ToText(data);
+}
+
+String
+XWindowsClipboardTextConverter::toIClipboard(const String& data) const
+{
+ // convert to UTF-8
+ bool errors;
+ String utf8 = Unicode::textToUTF8(data, &errors);
+
+ // if there were decoding errors then, to support old applications
+ // that don't understand UTF-8 but can report the exact binary
+ // UTF-8 representation, see if the data appears to be UTF-8. if
+ // so then use it as is.
+ if (errors && Unicode::isUTF8(data)) {
+ return data;
+ }
+
+ return utf8;
+}
diff --git a/src/lib/platform/XWindowsClipboardTextConverter.h b/src/lib/platform/XWindowsClipboardTextConverter.h
new file mode 100644
index 0000000..0e6d598
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardTextConverter.h
@@ -0,0 +1,42 @@
+/*
+ * barrier -- 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 "platform/XWindowsClipboard.h"
+
+//! Convert to/from locale text encoding
+class XWindowsClipboardTextConverter : public IXWindowsClipboardConverter {
+public:
+ /*!
+ \c name is converted to an atom and that is reported by getAtom().
+ */
+ XWindowsClipboardTextConverter(Display* display, const char* name);
+ virtual ~XWindowsClipboardTextConverter();
+
+ // IXWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual Atom getAtom() const;
+ virtual int getDataSize() const;
+ virtual String fromIClipboard(const String&) const;
+ virtual String toIClipboard(const String&) const;
+
+private:
+ Atom m_atom;
+};
diff --git a/src/lib/platform/XWindowsClipboardUCS2Converter.cpp b/src/lib/platform/XWindowsClipboardUCS2Converter.cpp
new file mode 100644
index 0000000..988b909
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardUCS2Converter.cpp
@@ -0,0 +1,67 @@
+/*
+ * barrier -- 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 "platform/XWindowsClipboardUCS2Converter.h"
+
+#include "base/Unicode.h"
+
+//
+// XWindowsClipboardUCS2Converter
+//
+
+XWindowsClipboardUCS2Converter::XWindowsClipboardUCS2Converter(
+ Display* display, const char* name) :
+ m_atom(XInternAtom(display, name, False))
+{
+ // do nothing
+}
+
+XWindowsClipboardUCS2Converter::~XWindowsClipboardUCS2Converter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+XWindowsClipboardUCS2Converter::getFormat() const
+{
+ return IClipboard::kText;
+}
+
+Atom
+XWindowsClipboardUCS2Converter::getAtom() const
+{
+ return m_atom;
+}
+
+int
+XWindowsClipboardUCS2Converter::getDataSize() const
+{
+ return 16;
+}
+
+String
+XWindowsClipboardUCS2Converter::fromIClipboard(const String& data) const
+{
+ return Unicode::UTF8ToUCS2(data);
+}
+
+String
+XWindowsClipboardUCS2Converter::toIClipboard(const String& data) const
+{
+ return Unicode::UCS2ToUTF8(data);
+}
diff --git a/src/lib/platform/XWindowsClipboardUCS2Converter.h b/src/lib/platform/XWindowsClipboardUCS2Converter.h
new file mode 100644
index 0000000..6491408
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardUCS2Converter.h
@@ -0,0 +1,42 @@
+/*
+ * barrier -- 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 "platform/XWindowsClipboard.h"
+
+//! Convert to/from UCS-2 encoding
+class XWindowsClipboardUCS2Converter : public IXWindowsClipboardConverter {
+public:
+ /*!
+ \c name is converted to an atom and that is reported by getAtom().
+ */
+ XWindowsClipboardUCS2Converter(Display* display, const char* name);
+ virtual ~XWindowsClipboardUCS2Converter();
+
+ // IXWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual Atom getAtom() const;
+ virtual int getDataSize() const;
+ virtual String fromIClipboard(const String&) const;
+ virtual String toIClipboard(const String&) const;
+
+private:
+ Atom m_atom;
+};
diff --git a/src/lib/platform/XWindowsClipboardUTF8Converter.cpp b/src/lib/platform/XWindowsClipboardUTF8Converter.cpp
new file mode 100644
index 0000000..0e43cce
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardUTF8Converter.cpp
@@ -0,0 +1,65 @@
+/*
+ * barrier -- 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 "platform/XWindowsClipboardUTF8Converter.h"
+
+//
+// XWindowsClipboardUTF8Converter
+//
+
+XWindowsClipboardUTF8Converter::XWindowsClipboardUTF8Converter(
+ Display* display, const char* name) :
+ m_atom(XInternAtom(display, name, False))
+{
+ // do nothing
+}
+
+XWindowsClipboardUTF8Converter::~XWindowsClipboardUTF8Converter()
+{
+ // do nothing
+}
+
+IClipboard::EFormat
+XWindowsClipboardUTF8Converter::getFormat() const
+{
+ return IClipboard::kText;
+}
+
+Atom
+XWindowsClipboardUTF8Converter::getAtom() const
+{
+ return m_atom;
+}
+
+int
+XWindowsClipboardUTF8Converter::getDataSize() const
+{
+ return 8;
+}
+
+String
+XWindowsClipboardUTF8Converter::fromIClipboard(const String& data) const
+{
+ return data;
+}
+
+String
+XWindowsClipboardUTF8Converter::toIClipboard(const String& data) const
+{
+ return data;
+}
diff --git a/src/lib/platform/XWindowsClipboardUTF8Converter.h b/src/lib/platform/XWindowsClipboardUTF8Converter.h
new file mode 100644
index 0000000..e3eeca0
--- /dev/null
+++ b/src/lib/platform/XWindowsClipboardUTF8Converter.h
@@ -0,0 +1,42 @@
+/*
+ * barrier -- 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 "platform/XWindowsClipboard.h"
+
+//! Convert to/from UTF-8 encoding
+class XWindowsClipboardUTF8Converter : public IXWindowsClipboardConverter {
+public:
+ /*!
+ \c name is converted to an atom and that is reported by getAtom().
+ */
+ XWindowsClipboardUTF8Converter(Display* display, const char* name);
+ virtual ~XWindowsClipboardUTF8Converter();
+
+ // IXWindowsClipboardConverter overrides
+ virtual IClipboard::EFormat
+ getFormat() const;
+ virtual Atom getAtom() const;
+ virtual int getDataSize() const;
+ virtual String fromIClipboard(const String&) const;
+ virtual String toIClipboard(const String&) const;
+
+private:
+ Atom m_atom;
+};
diff --git a/src/lib/platform/XWindowsEventQueueBuffer.cpp b/src/lib/platform/XWindowsEventQueueBuffer.cpp
new file mode 100644
index 0000000..234cd62
--- /dev/null
+++ b/src/lib/platform/XWindowsEventQueueBuffer.cpp
@@ -0,0 +1,291 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "platform/XWindowsEventQueueBuffer.h"
+
+#include "mt/Lock.h"
+#include "mt/Thread.h"
+#include "base/Event.h"
+#include "base/IEventQueue.h"
+
+#include <fcntl.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#if HAVE_POLL
+# include <poll.h>
+#else
+# if HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# endif
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# endif
+# if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+# endif
+#endif
+
+//
+// EventQueueTimer
+//
+
+class EventQueueTimer { };
+
+
+//
+// XWindowsEventQueueBuffer
+//
+
+XWindowsEventQueueBuffer::XWindowsEventQueueBuffer(
+ Display* display, Window window, IEventQueue* events) :
+ m_events(events),
+ m_display(display),
+ m_window(window),
+ m_waiting(false)
+{
+ assert(m_display != NULL);
+ assert(m_window != None);
+
+ m_userEvent = XInternAtom(m_display, "BARRIER_USER_EVENT", False);
+ // set up for pipe hack
+ int result = pipe(m_pipefd);
+ assert(result == 0);
+
+ int pipeflags;
+ pipeflags = fcntl(m_pipefd[0], F_GETFL);
+ fcntl(m_pipefd[0], F_SETFL, pipeflags | O_NONBLOCK);
+ pipeflags = fcntl(m_pipefd[1], F_GETFL);
+ fcntl(m_pipefd[1], F_SETFL, pipeflags | O_NONBLOCK);
+}
+
+XWindowsEventQueueBuffer::~XWindowsEventQueueBuffer()
+{
+ // release pipe hack resources
+ close(m_pipefd[0]);
+ close(m_pipefd[1]);
+}
+
+void
+XWindowsEventQueueBuffer::waitForEvent(double dtimeout)
+{
+ Thread::testCancel();
+
+ // clear out the pipe in preparation for waiting.
+
+ char buf[16];
+ ssize_t read_response = read(m_pipefd[0], buf, 15);
+
+ // with linux automake, warnings are treated as errors by default
+ if (read_response < 0)
+ {
+ // todo: handle read response
+ }
+
+ {
+ Lock lock(&m_mutex);
+ // we're now waiting for events
+ m_waiting = true;
+
+ // push out pending events
+ flush();
+ }
+ // calling flush may have queued up a new event.
+ if (!XWindowsEventQueueBuffer::isEmpty()) {
+ Thread::testCancel();
+ return;
+ }
+
+ // use poll() to wait for a message from the X server or for timeout.
+ // this is a good deal more efficient than polling and sleeping.
+#if HAVE_POLL
+ struct pollfd pfds[2];
+ pfds[0].fd = ConnectionNumber(m_display);
+ pfds[0].events = POLLIN;
+ pfds[1].fd = m_pipefd[0];
+ pfds[1].events = POLLIN;
+ int timeout = (dtimeout < 0.0) ? -1 :
+ static_cast<int>(1000.0 * dtimeout);
+ int remaining = timeout;
+ int retval = 0;
+#else
+ struct timeval timeout;
+ struct timeval* timeoutPtr;
+ if (dtimeout < 0.0) {
+ timeoutPtr = NULL;
+ }
+ else {
+ timeout.tv_sec = static_cast<int>(dtimeout);
+ timeout.tv_usec = static_cast<int>(1.0e+6 *
+ (dtimeout - timeout.tv_sec));
+ timeoutPtr = &timeout;
+ }
+
+ // initialize file descriptor sets
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(ConnectionNumber(m_display), &rfds);
+ FD_SET(m_pipefd[0], &rfds);
+ int nfds;
+ if (ConnectionNumber(m_display) > m_pipefd[0]) {
+ nfds = ConnectionNumber(m_display) + 1;
+ }
+ else {
+ nfds = m_pipefd[0] + 1;
+ }
+#endif
+ // It's possible that the X server has queued events locally
+ // in xlib's event buffer and not pushed on to the fd. Hence we
+ // can't simply monitor the fd as we may never be woken up.
+ // ie addEvent calls flush, XFlush may not send via the fd hence
+ // there is an event waiting to be sent but we must exit the poll
+ // before it can.
+ // Instead we poll for a brief period of time (so if events
+ // queued locally in the xlib buffer can be processed)
+ // and continue doing this until timeout is reached.
+ // The human eye can notice 60hz (ansi) which is 16ms, however
+ // we want to give the cpu a chance s owe up this to 25ms
+#define TIMEOUT_DELAY 25
+
+ while (((dtimeout < 0.0) || (remaining > 0)) && QLength(m_display)==0 && retval==0){
+#if HAVE_POLL
+ retval = poll(pfds, 2, TIMEOUT_DELAY); //16ms = 60hz, but we make it > to play nicely with the cpu
+ if (pfds[1].revents & POLLIN) {
+ ssize_t read_response = read(m_pipefd[0], buf, 15);
+
+ // with linux automake, warnings are treated as errors by default
+ if (read_response < 0)
+ {
+ // todo: handle read response
+ }
+
+ }
+#else
+ retval = select(nfds,
+ SELECT_TYPE_ARG234 &rfds,
+ SELECT_TYPE_ARG234 NULL,
+ SELECT_TYPE_ARG234 NULL,
+ SELECT_TYPE_ARG5 TIMEOUT_DELAY);
+ if (FD_SET(m_pipefd[0], &rfds)) {
+ read(m_pipefd[0], buf, 15);
+ }
+#endif
+ remaining-=TIMEOUT_DELAY;
+ }
+
+ {
+ // we're no longer waiting for events
+ Lock lock(&m_mutex);
+ m_waiting = false;
+ }
+
+ Thread::testCancel();
+}
+
+IEventQueueBuffer::Type
+XWindowsEventQueueBuffer::getEvent(Event& event, UInt32& dataID)
+{
+ Lock lock(&m_mutex);
+
+ // push out pending events
+ flush();
+
+ // get next event
+ XNextEvent(m_display, &m_event);
+
+ // process event
+ if (m_event.xany.type == ClientMessage &&
+ m_event.xclient.message_type == m_userEvent) {
+ dataID = static_cast<UInt32>(m_event.xclient.data.l[0]);
+ return kUser;
+ }
+ else {
+ event = Event(Event::kSystem,
+ m_events->getSystemTarget(), &m_event);
+ return kSystem;
+ }
+}
+
+bool
+XWindowsEventQueueBuffer::addEvent(UInt32 dataID)
+{
+ // prepare a message
+ XEvent xevent;
+ xevent.xclient.type = ClientMessage;
+ xevent.xclient.window = m_window;
+ xevent.xclient.message_type = m_userEvent;
+ xevent.xclient.format = 32;
+ xevent.xclient.data.l[0] = static_cast<long>(dataID);
+
+ // save the message
+ Lock lock(&m_mutex);
+ m_postedEvents.push_back(xevent);
+
+ // if we're currently waiting for an event then send saved events to
+ // the X server now. if we're not waiting then some other thread
+ // might be using the display connection so we can't safely use it
+ // too.
+ if (m_waiting) {
+ flush();
+ // Send a character through the round-trip pipe to wake a thread
+ // that is waiting for a ConnectionNumber() socket to be readable.
+ // The flush call can read incoming data from the socket and put
+ // it in Xlib's input buffer. That sneaks it past the other thread.
+ ssize_t write_response = write(m_pipefd[1], "!", 1);
+
+ // with linux automake, warnings are treated as errors by default
+ if (write_response < 0)
+ {
+ // todo: handle read response
+ }
+ }
+
+ return true;
+}
+
+bool
+XWindowsEventQueueBuffer::isEmpty() const
+{
+ Lock lock(&m_mutex);
+ return (XPending(m_display) == 0 );
+}
+
+EventQueueTimer*
+XWindowsEventQueueBuffer::newTimer(double, bool) const
+{
+ return new EventQueueTimer;
+}
+
+void
+XWindowsEventQueueBuffer::deleteTimer(EventQueueTimer* timer) const
+{
+ delete timer;
+}
+
+void
+XWindowsEventQueueBuffer::flush()
+{
+ // note -- m_mutex must be locked on entry
+
+ // flush the posted event list to the X server
+ for (size_t i = 0; i < m_postedEvents.size(); ++i) {
+ XSendEvent(m_display, m_window, False, 0, &m_postedEvents[i]);
+ }
+ XFlush(m_display);
+ m_postedEvents.clear();
+}
diff --git a/src/lib/platform/XWindowsEventQueueBuffer.h b/src/lib/platform/XWindowsEventQueueBuffer.h
new file mode 100644
index 0000000..07f3b3a
--- /dev/null
+++ b/src/lib/platform/XWindowsEventQueueBuffer.h
@@ -0,0 +1,64 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "mt/Mutex.h"
+#include "base/IEventQueueBuffer.h"
+#include "common/stdvector.h"
+
+#if X_DISPLAY_MISSING
+# error X11 is required to build barrier
+#else
+# include <X11/Xlib.h>
+#endif
+
+class IEventQueue;
+
+//! Event queue buffer for X11
+class XWindowsEventQueueBuffer : public IEventQueueBuffer {
+public:
+ XWindowsEventQueueBuffer(Display*, Window, IEventQueue* events);
+ virtual ~XWindowsEventQueueBuffer();
+
+ // IEventQueueBuffer overrides
+ virtual void init() { }
+ virtual void waitForEvent(double timeout);
+ virtual Type getEvent(Event& event, UInt32& dataID);
+ virtual bool addEvent(UInt32 dataID);
+ virtual bool isEmpty() const;
+ virtual EventQueueTimer*
+ newTimer(double duration, bool oneShot) const;
+ virtual void deleteTimer(EventQueueTimer*) const;
+
+private:
+ void flush();
+
+private:
+ typedef std::vector<XEvent> EventList;
+
+ Mutex m_mutex;
+ Display* m_display;
+ Window m_window;
+ Atom m_userEvent;
+ XEvent m_event;
+ EventList m_postedEvents;
+ bool m_waiting;
+ int m_pipefd[2];
+ IEventQueue* m_events;
+};
diff --git a/src/lib/platform/XWindowsKeyState.cpp b/src/lib/platform/XWindowsKeyState.cpp
new file mode 100644
index 0000000..1ca4629
--- /dev/null
+++ b/src/lib/platform/XWindowsKeyState.cpp
@@ -0,0 +1,867 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "platform/XWindowsKeyState.h"
+
+#include "platform/XWindowsUtil.h"
+#include "base/Log.h"
+#include "base/String.h"
+#include "common/stdmap.h"
+
+#include <cstddef>
+#include <algorithm>
+#if X_DISPLAY_MISSING
+# error X11 is required to build barrier
+#else
+# include <X11/X.h>
+# include <X11/Xutil.h>
+# define XK_MISCELLANY
+# define XK_XKB_KEYS
+# include <X11/keysymdef.h>
+#if HAVE_XKB_EXTENSION
+# include <X11/XKBlib.h>
+#endif
+#endif
+
+static const size_t ModifiersFromXDefaultSize = 32;
+
+XWindowsKeyState::XWindowsKeyState(
+ Display* display, bool useXKB,
+ IEventQueue* events) :
+ KeyState(events),
+ m_display(display),
+ m_modifierFromX(ModifiersFromXDefaultSize)
+{
+ init(display, useXKB);
+}
+
+XWindowsKeyState::XWindowsKeyState(
+ Display* display, bool useXKB,
+ IEventQueue* events, barrier::KeyMap& keyMap) :
+ KeyState(events, keyMap),
+ m_display(display),
+ m_modifierFromX(ModifiersFromXDefaultSize)
+{
+ init(display, useXKB);
+}
+
+XWindowsKeyState::~XWindowsKeyState()
+{
+#if HAVE_XKB_EXTENSION
+ if (m_xkb != NULL) {
+ XkbFreeKeyboard(m_xkb, 0, True);
+ }
+#endif
+}
+
+void
+XWindowsKeyState::init(Display* display, bool useXKB)
+{
+ XGetKeyboardControl(m_display, &m_keyboardState);
+#if HAVE_XKB_EXTENSION
+ if (useXKB) {
+ m_xkb = XkbGetMap(m_display, XkbKeyActionsMask | XkbKeyBehaviorsMask |
+ XkbAllClientInfoMask, XkbUseCoreKbd);
+ }
+ else {
+ m_xkb = NULL;
+ }
+#endif
+ setActiveGroup(kGroupPollAndSet);
+}
+
+void
+XWindowsKeyState::setActiveGroup(SInt32 group)
+{
+ if (group == kGroupPollAndSet) {
+ // we need to set the group to -1 in order for pollActiveGroup() to
+ // actually poll for the group
+ m_group = -1;
+ m_group = pollActiveGroup();
+ }
+ else if (group == kGroupPoll) {
+ m_group = -1;
+ }
+ else {
+ assert(group >= 0);
+ m_group = group;
+ }
+}
+
+void
+XWindowsKeyState::setAutoRepeat(const XKeyboardState& state)
+{
+ m_keyboardState = state;
+}
+
+KeyModifierMask
+XWindowsKeyState::mapModifiersFromX(unsigned int state) const
+{
+ LOG((CLOG_DEBUG2 "mapping state: %i", state));
+ UInt32 offset = 8 * getGroupFromState(state);
+ KeyModifierMask mask = 0;
+ for (int i = 0; i < 8; ++i) {
+ if ((state & (1u << i)) != 0) {
+ LOG((CLOG_DEBUG2 "|= modifier: %i", offset + i));
+ if (offset + i >= m_modifierFromX.size()) {
+ LOG((CLOG_ERR "m_modifierFromX is too small (%d) for the "
+ "requested offset (%d)", m_modifierFromX.size(), offset+i));
+ } else {
+ mask |= m_modifierFromX[offset + i];
+ }
+ }
+ }
+ return mask;
+}
+
+bool
+XWindowsKeyState::mapModifiersToX(KeyModifierMask mask,
+ unsigned int& modifiers) const
+{
+ modifiers = 0;
+
+ for (SInt32 i = 0; i < kKeyModifierNumBits; ++i) {
+ KeyModifierMask bit = (1u << i);
+ if ((mask & bit) != 0) {
+ KeyModifierToXMask::const_iterator j = m_modifierToX.find(bit);
+ if (j == m_modifierToX.end()) {
+ return false;
+ }
+ else {
+ modifiers |= j->second;
+ }
+ }
+ }
+
+ return true;
+}
+
+void
+XWindowsKeyState::mapKeyToKeycodes(KeyID key, KeycodeList& keycodes) const
+{
+ keycodes.clear();
+ std::pair<KeyToKeyCodeMap::const_iterator,
+ KeyToKeyCodeMap::const_iterator> range =
+ m_keyCodeFromKey.equal_range(key);
+ for (KeyToKeyCodeMap::const_iterator i = range.first;
+ i != range.second; ++i) {
+ keycodes.push_back(i->second);
+ }
+}
+
+bool
+XWindowsKeyState::fakeCtrlAltDel()
+{
+ // pass keys through unchanged
+ return false;
+}
+
+KeyModifierMask
+XWindowsKeyState::pollActiveModifiers() const
+{
+ Window root = DefaultRootWindow(m_display), window;
+ int xRoot, yRoot, xWindow, yWindow;
+ unsigned int state = 0;
+ if (XQueryPointer(m_display, root, &root, &window,
+ &xRoot, &yRoot, &xWindow, &yWindow, &state) == False) {
+ state = 0;
+ }
+ return mapModifiersFromX(state);
+}
+
+SInt32
+XWindowsKeyState::pollActiveGroup() const
+{
+ // fixed condition where any group < -1 would have undetermined behaviour
+ if (m_group >= 0) {
+ return m_group;
+ }
+
+#if HAVE_XKB_EXTENSION
+ if (m_xkb != NULL) {
+ XkbStateRec state;
+ if (XkbGetState(m_display, XkbUseCoreKbd, &state) == Success) {
+ return state.group;
+ }
+ }
+#endif
+ return 0;
+}
+
+void
+XWindowsKeyState::pollPressedKeys(KeyButtonSet& pressedKeys) const
+{
+ char keys[32];
+ XQueryKeymap(m_display, keys);
+ for (UInt32 i = 0; i < 32; ++i) {
+ for (UInt32 j = 0; j < 8; ++j) {
+ if ((keys[i] & (1u << j)) != 0) {
+ pressedKeys.insert(8 * i + j);
+ }
+ }
+ }
+}
+
+void
+XWindowsKeyState::getKeyMap(barrier::KeyMap& keyMap)
+{
+ // get autorepeat info. we must use the global_auto_repeat told to
+ // us because it may have modified by barrier.
+ int oldGlobalAutoRepeat = m_keyboardState.global_auto_repeat;
+ XGetKeyboardControl(m_display, &m_keyboardState);
+ m_keyboardState.global_auto_repeat = oldGlobalAutoRepeat;
+
+#if HAVE_XKB_EXTENSION
+ if (m_xkb != NULL) {
+ if (XkbGetUpdatedMap(m_display, XkbKeyActionsMask |
+ XkbKeyBehaviorsMask | XkbAllClientInfoMask, m_xkb) == Success) {
+ updateKeysymMapXKB(keyMap);
+ return;
+ }
+ }
+#endif
+ updateKeysymMap(keyMap);
+}
+
+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"));
+ if (keystroke.m_data.m_button.m_repeat) {
+ int c = keystroke.m_data.m_button.m_button;
+ int i = (c >> 3);
+ int b = 1 << (c & 7);
+ if (m_keyboardState.global_auto_repeat == AutoRepeatModeOff ||
+ (c!=113 && c!=116 && (m_keyboardState.auto_repeats[i] & b) == 0)) {
+ LOG((CLOG_DEBUG1 " discard autorepeat"));
+ break;
+ }
+ }
+ XTestFakeKeyEvent(m_display, keystroke.m_data.m_button.m_button,
+ keystroke.m_data.m_button.m_press ? True : False,
+ CurrentTime);
+ break;
+
+ case Keystroke::kGroup:
+ if (keystroke.m_data.m_group.m_absolute) {
+ LOG((CLOG_DEBUG1 " group %d", keystroke.m_data.m_group.m_group));
+#if HAVE_XKB_EXTENSION
+ if (m_xkb != NULL) {
+ if (XkbLockGroup(m_display, XkbUseCoreKbd,
+ keystroke.m_data.m_group.m_group) == False) {
+ LOG((CLOG_DEBUG1 "XkbLockGroup request not sent"));
+ }
+ }
+ else
+#endif
+ {
+ LOG((CLOG_DEBUG1 " ignored"));
+ }
+ }
+ else {
+ LOG((CLOG_DEBUG1 " group %+d", keystroke.m_data.m_group.m_group));
+#if HAVE_XKB_EXTENSION
+ if (m_xkb != NULL) {
+ if (XkbLockGroup(m_display, XkbUseCoreKbd,
+ getEffectiveGroup(pollActiveGroup(),
+ keystroke.m_data.m_group.m_group)) == False) {
+ LOG((CLOG_DEBUG1 "XkbLockGroup request not sent"));
+ }
+ }
+ else
+#endif
+ {
+ LOG((CLOG_DEBUG1 " ignored"));
+ }
+ }
+ break;
+ }
+ XFlush(m_display);
+}
+
+void
+XWindowsKeyState::updateKeysymMap(barrier::KeyMap& keyMap)
+{
+ // there are up to 4 keysyms per keycode
+ static const int maxKeysyms = 4;
+
+ LOG((CLOG_DEBUG1 "non-XKB mapping"));
+
+ // prepare map from X modifier to KeyModifierMask. certain bits
+ // are predefined.
+ std::fill(m_modifierFromX.begin(), m_modifierFromX.end(), 0);
+ m_modifierFromX[ShiftMapIndex] = KeyModifierShift;
+ m_modifierFromX[LockMapIndex] = KeyModifierCapsLock;
+ m_modifierFromX[ControlMapIndex] = KeyModifierControl;
+ m_modifierToX.clear();
+ m_modifierToX[KeyModifierShift] = ShiftMask;
+ m_modifierToX[KeyModifierCapsLock] = LockMask;
+ m_modifierToX[KeyModifierControl] = ControlMask;
+
+ // prepare map from KeyID to KeyCode
+ m_keyCodeFromKey.clear();
+
+ // get the number of keycodes
+ int minKeycode, maxKeycode;
+ XDisplayKeycodes(m_display, &minKeycode, &maxKeycode);
+ int numKeycodes = maxKeycode - minKeycode + 1;
+
+ // get the keyboard mapping for all keys
+ int keysymsPerKeycode;
+ KeySym* allKeysyms = XGetKeyboardMapping(m_display,
+ minKeycode, numKeycodes,
+ &keysymsPerKeycode);
+
+ // it's more convenient to always have maxKeysyms KeySyms per key
+ {
+ KeySym* tmpKeysyms = new KeySym[maxKeysyms * numKeycodes];
+ for (int i = 0; i < numKeycodes; ++i) {
+ for (int j = 0; j < maxKeysyms; ++j) {
+ if (j < keysymsPerKeycode) {
+ tmpKeysyms[maxKeysyms * i + j] =
+ allKeysyms[keysymsPerKeycode * i + j];
+ }
+ else {
+ tmpKeysyms[maxKeysyms * i + j] = NoSymbol;
+ }
+ }
+ }
+ XFree(allKeysyms);
+ allKeysyms = tmpKeysyms;
+ }
+
+ // get the buttons assigned to modifiers. X11 does not predefine
+ // the meaning of any modifiers except shift, caps lock, and the
+ // control key. the meaning of a modifier bit (other than those)
+ // depends entirely on the KeySyms mapped to that bit. unfortunately
+ // you cannot map a bit back to the KeySym used to produce it.
+ // for example, let's say button 1 maps to Alt_L without shift and
+ // Meta_L with shift. now if mod1 is mapped to button 1 that could
+ // mean the user used Alt or Meta to turn on that modifier and there's
+ // no way to know which. it's also possible for one button to be
+ // mapped to multiple bits so both mod1 and mod2 could be generated
+ // by button 1.
+ //
+ // we're going to ignore any modifier for a button except the first.
+ // with the above example, that means we'll ignore the mod2 modifier
+ // bit unless it's also mapped to some other button. we're also
+ // going to ignore all KeySyms except the first modifier KeySym,
+ // which means button 1 above won't map to Meta, just Alt.
+ std::map<KeyCode, unsigned int> modifierButtons;
+ XModifierKeymap* modifiers = XGetModifierMapping(m_display);
+ for (unsigned int i = 0; i < 8; ++i) {
+ const KeyCode* buttons =
+ modifiers->modifiermap + i * modifiers->max_keypermod;
+ for (int j = 0; j < modifiers->max_keypermod; ++j) {
+ modifierButtons.insert(std::make_pair(buttons[j], i));
+ }
+ }
+ XFreeModifiermap(modifiers);
+ modifierButtons.erase(0);
+
+ // Hack to deal with VMware. When a VMware client grabs input the
+ // player clears out the X modifier map for whatever reason. We're
+ // notified of the change and arrive here to discover that there
+ // are no modifiers at all. Since this prevents the modifiers from
+ // working in the VMware client we'll use the last known good set
+ // of modifiers when there are no modifiers. If there are modifiers
+ // we update the last known good set.
+ if (!modifierButtons.empty()) {
+ m_lastGoodNonXKBModifiers = modifierButtons;
+ }
+ else {
+ modifierButtons = m_lastGoodNonXKBModifiers;
+ }
+
+ // add entries for each keycode
+ barrier::KeyMap::KeyItem item;
+ for (int i = 0; i < numKeycodes; ++i) {
+ KeySym* keysyms = allKeysyms + maxKeysyms * i;
+ KeyCode keycode = static_cast<KeyCode>(i + minKeycode);
+ item.m_button = static_cast<KeyButton>(keycode);
+ item.m_client = 0;
+
+ // determine modifier sensitivity
+ item.m_sensitive = 0;
+
+ // if the keysyms in levels 2 or 3 exist and differ from levels
+ // 0 and 1 then the key is sensitive AltGr (Mode_switch)
+ if ((keysyms[2] != NoSymbol && keysyms[2] != keysyms[0]) ||
+ (keysyms[3] != NoSymbol && keysyms[2] != keysyms[1])) {
+ item.m_sensitive |= KeyModifierAltGr;
+ }
+
+ // check if the key is caps-lock sensitive. some systems only
+ // provide one keysym for keys sensitive to caps-lock. if we
+ // find that then fill in the missing keysym.
+ if (keysyms[0] != NoSymbol && keysyms[1] == NoSymbol &&
+ keysyms[2] == NoSymbol && keysyms[3] == NoSymbol) {
+ KeySym lKeysym, uKeysym;
+ XConvertCase(keysyms[0], &lKeysym, &uKeysym);
+ if (lKeysym != uKeysym) {
+ keysyms[0] = lKeysym;
+ keysyms[1] = uKeysym;
+ item.m_sensitive |= KeyModifierCapsLock;
+ }
+ }
+ else if (keysyms[0] != NoSymbol && keysyms[1] != NoSymbol) {
+ KeySym lKeysym, uKeysym;
+ XConvertCase(keysyms[0], &lKeysym, &uKeysym);
+ if (lKeysym != uKeysym &&
+ lKeysym == keysyms[0] &&
+ uKeysym == keysyms[1]) {
+ item.m_sensitive |= KeyModifierCapsLock;
+ }
+ else if (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol) {
+ XConvertCase(keysyms[2], &lKeysym, &uKeysym);
+ if (lKeysym != uKeysym &&
+ lKeysym == keysyms[2] &&
+ uKeysym == keysyms[3]) {
+ item.m_sensitive |= KeyModifierCapsLock;
+ }
+ }
+ }
+
+ // key is sensitive to shift if keysyms in levels 0 and 1 or
+ // levels 2 and 3 don't match. it's also sensitive to shift
+ // if it's sensitive to caps-lock.
+ if ((item.m_sensitive & KeyModifierCapsLock) != 0) {
+ item.m_sensitive |= KeyModifierShift;
+ }
+ else if ((keysyms[0] != NoSymbol && keysyms[1] != NoSymbol &&
+ keysyms[0] != keysyms[1]) ||
+ (keysyms[2] != NoSymbol && keysyms[3] != NoSymbol &&
+ keysyms[2] != keysyms[3])) {
+ item.m_sensitive |= KeyModifierShift;
+ }
+
+ // key is sensitive to numlock if any keysym on it is
+ if (IsKeypadKey(keysyms[0]) || IsPrivateKeypadKey(keysyms[0]) ||
+ IsKeypadKey(keysyms[1]) || IsPrivateKeypadKey(keysyms[1]) ||
+ IsKeypadKey(keysyms[2]) || IsPrivateKeypadKey(keysyms[2]) ||
+ IsKeypadKey(keysyms[3]) || IsPrivateKeypadKey(keysyms[3])) {
+ item.m_sensitive |= KeyModifierNumLock;
+ }
+
+ // do each keysym (shift level)
+ for (int j = 0; j < maxKeysyms; ++j) {
+ item.m_id = XWindowsUtil::mapKeySymToKeyID(keysyms[j]);
+ if (item.m_id == kKeyNone) {
+ if (j != 0 && modifierButtons.count(keycode) > 0) {
+ // pretend the modifier works in other shift levels
+ // because it probably does.
+ if (keysyms[1] == NoSymbol || j != 3) {
+ item.m_id = XWindowsUtil::mapKeySymToKeyID(keysyms[0]);
+ }
+ else {
+ item.m_id = XWindowsUtil::mapKeySymToKeyID(keysyms[1]);
+ }
+ }
+ if (item.m_id == kKeyNone) {
+ continue;
+ }
+ }
+
+ // group is 0 for levels 0 and 1 and 1 for levels 2 and 3
+ item.m_group = (j >= 2) ? 1 : 0;
+
+ // compute required modifiers
+ item.m_required = 0;
+ if ((j & 1) != 0) {
+ item.m_required |= KeyModifierShift;
+ }
+ if ((j & 2) != 0) {
+ item.m_required |= KeyModifierAltGr;
+ }
+
+ item.m_generates = 0;
+ item.m_lock = false;
+ if (modifierButtons.count(keycode) > 0) {
+ // get flags for modifier keys
+ barrier::KeyMap::initModifierKey(item);
+
+ // add mapping from X (unless we already have)
+ if (item.m_generates != 0) {
+ unsigned int bit = modifierButtons[keycode];
+ if (m_modifierFromX[bit] == 0) {
+ m_modifierFromX[bit] = item.m_generates;
+ m_modifierToX[item.m_generates] = (1u << bit);
+ }
+ }
+ }
+
+ // add key
+ keyMap.addKeyEntry(item);
+ m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode));
+
+ // add other ways to synthesize the key
+ if ((j & 1) != 0) {
+ // add capslock version of key is sensitive to capslock
+ KeySym lKeysym, uKeysym;
+ XConvertCase(keysyms[j], &lKeysym, &uKeysym);
+ if (lKeysym != uKeysym &&
+ lKeysym == keysyms[j - 1] &&
+ uKeysym == keysyms[j]) {
+ item.m_required &= ~KeyModifierShift;
+ item.m_required |= KeyModifierCapsLock;
+ keyMap.addKeyEntry(item);
+ item.m_required |= KeyModifierShift;
+ item.m_required &= ~KeyModifierCapsLock;
+ }
+
+ // add numlock version of key if sensitive to numlock
+ if (IsKeypadKey(keysyms[j]) || IsPrivateKeypadKey(keysyms[j])) {
+ item.m_required &= ~KeyModifierShift;
+ item.m_required |= KeyModifierNumLock;
+ keyMap.addKeyEntry(item);
+ item.m_required |= KeyModifierShift;
+ item.m_required &= ~KeyModifierNumLock;
+ }
+ }
+ }
+ }
+
+ delete[] allKeysyms;
+}
+
+#if HAVE_XKB_EXTENSION
+void
+XWindowsKeyState::updateKeysymMapXKB(barrier::KeyMap& keyMap)
+{
+ static const XkbKTMapEntryRec defMapEntry = {
+ True, // active
+ 0, // level
+ {
+ 0, // mods.mask
+ 0, // mods.real_mods
+ 0 // mods.vmods
+ }
+ };
+
+ LOG((CLOG_DEBUG1 "XKB mapping"));
+
+ // find the number of groups
+ int maxNumGroups = 0;
+ for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
+ int numGroups = XkbKeyNumGroups(m_xkb, static_cast<KeyCode>(i));
+ if (numGroups > maxNumGroups) {
+ maxNumGroups = numGroups;
+ }
+ }
+
+ // prepare map from X modifier to KeyModifierMask
+ std::vector<int> modifierLevel(maxNumGroups * 8, 4);
+ m_modifierFromX.clear();
+ m_modifierFromX.resize(maxNumGroups * 8);
+ m_modifierToX.clear();
+
+ // prepare map from KeyID to KeyCode
+ m_keyCodeFromKey.clear();
+
+ // Hack to deal with VMware. When a VMware client grabs input the
+ // player clears out the X modifier map for whatever reason. We're
+ // notified of the change and arrive here to discover that there
+ // are no modifiers at all. Since this prevents the modifiers from
+ // working in the VMware client we'll use the last known good set
+ // of modifiers when there are no modifiers. If there are modifiers
+ // we update the last known good set.
+ bool useLastGoodModifiers = !hasModifiersXKB();
+ if (!useLastGoodModifiers) {
+ m_lastGoodXKBModifiers.clear();
+ }
+
+ // check every button. on this pass we save all modifiers as native
+ // X modifier masks.
+ barrier::KeyMap::KeyItem item;
+ for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
+ KeyCode keycode = static_cast<KeyCode>(i);
+ item.m_button = static_cast<KeyButton>(keycode);
+ item.m_client = 0;
+
+ // skip keys with no groups (they generate no symbols)
+ if (XkbKeyNumGroups(m_xkb, keycode) == 0) {
+ continue;
+ }
+
+ // note half-duplex keys
+ const XkbBehavior& b = m_xkb->server->behaviors[keycode];
+ if ((b.type & XkbKB_OpMask) == XkbKB_Lock) {
+ keyMap.addHalfDuplexButton(item.m_button);
+ }
+
+ // iterate over all groups
+ for (int group = 0; group < maxNumGroups; ++group) {
+ item.m_group = group;
+ int eGroup = getEffectiveGroup(keycode, group);
+
+ // get key info
+ XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, eGroup);
+
+ // set modifiers the item is sensitive to
+ item.m_sensitive = type->mods.mask;
+
+ // iterate over all shift levels for the button (including none)
+ for (int j = -1; j < type->map_count; ++j) {
+ const XkbKTMapEntryRec* mapEntry =
+ ((j == -1) ? &defMapEntry : type->map + j);
+ if (!mapEntry->active) {
+ continue;
+ }
+ int level = mapEntry->level;
+
+ // set required modifiers for this item
+ item.m_required = mapEntry->mods.mask;
+ if ((item.m_required & LockMask) != 0 &&
+ j != -1 && type->preserve != NULL &&
+ (type->preserve[j].mask & LockMask) != 0) {
+ // sensitive caps lock and we preserve caps-lock.
+ // preserving caps-lock means we Xlib functions would
+ // yield the capitialized KeySym so we'll adjust the
+ // level accordingly.
+ if ((level ^ 1) < type->num_levels) {
+ level ^= 1;
+ }
+ }
+
+ // get the keysym for this item
+ KeySym keysym = XkbKeySymEntry(m_xkb, keycode, level, eGroup);
+
+ // check for group change actions, locking modifiers, and
+ // modifier masks.
+ item.m_lock = false;
+ bool isModifier = false;
+ UInt32 modifierMask = m_xkb->map->modmap[keycode];
+ if (XkbKeyHasActions(m_xkb, keycode) == True) {
+ XkbAction* action =
+ XkbKeyActionEntry(m_xkb, keycode, level, eGroup);
+ if (action->type == XkbSA_SetMods ||
+ action->type == XkbSA_LockMods) {
+ isModifier = true;
+
+ // note toggles
+ item.m_lock = (action->type == XkbSA_LockMods);
+
+ // maybe use action's mask
+ if ((action->mods.flags & XkbSA_UseModMapMods) == 0) {
+ modifierMask = action->mods.mask;
+ }
+ }
+ else if (action->type == XkbSA_SetGroup ||
+ action->type == XkbSA_LatchGroup ||
+ action->type == XkbSA_LockGroup) {
+ // ignore group change key
+ continue;
+ }
+ }
+ level = mapEntry->level;
+
+ // VMware modifier hack
+ if (useLastGoodModifiers) {
+ XKBModifierMap::const_iterator k =
+ m_lastGoodXKBModifiers.find(eGroup * 256 + keycode);
+ if (k != m_lastGoodXKBModifiers.end()) {
+ // Use last known good modifier
+ isModifier = true;
+ level = k->second.m_level;
+ modifierMask = k->second.m_mask;
+ item.m_lock = k->second.m_lock;
+ }
+ }
+ else if (isModifier) {
+ // Save known good modifier
+ XKBModifierInfo& info =
+ m_lastGoodXKBModifiers[eGroup * 256 + keycode];
+ info.m_level = level;
+ info.m_mask = modifierMask;
+ info.m_lock = item.m_lock;
+ }
+
+ // record the modifier mask for this key. don't bother
+ // for keys that change the group.
+ item.m_generates = 0;
+ UInt32 modifierBit =
+ XWindowsUtil::getModifierBitForKeySym(keysym);
+ if (isModifier && modifierBit != kKeyModifierBitNone) {
+ item.m_generates = (1u << modifierBit);
+ for (SInt32 j = 0; j < 8; ++j) {
+ // skip modifiers this key doesn't generate
+ if ((modifierMask & (1u << j)) == 0) {
+ continue;
+ }
+
+ // skip keys that map to a modifier that we've
+ // already seen using fewer modifiers. that is
+ // if this key must combine with other modifiers
+ // and we know of a key that combines with fewer
+ // modifiers (or no modifiers) then prefer the
+ // other key.
+ if (level >= modifierLevel[8 * group + j]) {
+ continue;
+ }
+ modifierLevel[8 * group + j] = level;
+
+ // save modifier
+ m_modifierFromX[8 * group + j] |= (1u << modifierBit);
+ m_modifierToX.insert(std::make_pair(
+ 1u << modifierBit, 1u << j));
+ }
+ }
+
+ // handle special cases of just one keysym for the keycode
+ if (type->num_levels == 1) {
+ // if there are upper- and lowercase versions of the
+ // keysym then add both.
+ KeySym lKeysym, uKeysym;
+ XConvertCase(keysym, &lKeysym, &uKeysym);
+ if (lKeysym != uKeysym) {
+ if (j != -1) {
+ continue;
+ }
+
+ item.m_sensitive |= ShiftMask | LockMask;
+
+ KeyID lKeyID = XWindowsUtil::mapKeySymToKeyID(lKeysym);
+ KeyID uKeyID = XWindowsUtil::mapKeySymToKeyID(uKeysym);
+ if (lKeyID == kKeyNone || uKeyID == kKeyNone) {
+ continue;
+ }
+
+ item.m_id = lKeyID;
+ item.m_required = 0;
+ keyMap.addKeyEntry(item);
+
+ item.m_id = uKeyID;
+ item.m_required = ShiftMask;
+ keyMap.addKeyEntry(item);
+ item.m_required = LockMask;
+ keyMap.addKeyEntry(item);
+
+ if (group == 0) {
+ m_keyCodeFromKey.insert(
+ std::make_pair(lKeyID, keycode));
+ m_keyCodeFromKey.insert(
+ std::make_pair(uKeyID, keycode));
+ }
+ continue;
+ }
+ }
+
+ // add entry
+ item.m_id = XWindowsUtil::mapKeySymToKeyID(keysym);
+ keyMap.addKeyEntry(item);
+ if (group == 0) {
+ m_keyCodeFromKey.insert(std::make_pair(item.m_id, keycode));
+ }
+ }
+ }
+ }
+
+ // change all modifier masks to barrier masks from X masks
+ keyMap.foreachKey(&XWindowsKeyState::remapKeyModifiers, this);
+
+ // allow composition across groups
+ keyMap.allowGroupSwitchDuringCompose();
+}
+#endif
+
+void
+XWindowsKeyState::remapKeyModifiers(KeyID id, SInt32 group,
+ barrier::KeyMap::KeyItem& item, void* vself)
+{
+ XWindowsKeyState* self = static_cast<XWindowsKeyState*>(vself);
+ item.m_required =
+ self->mapModifiersFromX(XkbBuildCoreState(item.m_required, group));
+ item.m_sensitive =
+ self->mapModifiersFromX(XkbBuildCoreState(item.m_sensitive, group));
+}
+
+bool
+XWindowsKeyState::hasModifiersXKB() const
+{
+#if HAVE_XKB_EXTENSION
+ // iterate over all keycodes
+ for (int i = m_xkb->min_key_code; i <= m_xkb->max_key_code; ++i) {
+ KeyCode keycode = static_cast<KeyCode>(i);
+ if (XkbKeyHasActions(m_xkb, keycode) == True) {
+ // iterate over all groups
+ int numGroups = XkbKeyNumGroups(m_xkb, keycode);
+ for (int group = 0; group < numGroups; ++group) {
+ // iterate over all shift levels for the button (including none)
+ XkbKeyTypePtr type = XkbKeyKeyType(m_xkb, keycode, group);
+ for (int j = -1; j < type->map_count; ++j) {
+ if (j != -1 && !type->map[j].active) {
+ continue;
+ }
+ int level = ((j == -1) ? 0 : type->map[j].level);
+ XkbAction* action =
+ XkbKeyActionEntry(m_xkb, keycode, level, group);
+ if (action->type == XkbSA_SetMods ||
+ action->type == XkbSA_LockMods) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+#endif
+ return false;
+}
+
+int
+XWindowsKeyState::getEffectiveGroup(KeyCode keycode, int group) const
+{
+ (void)keycode;
+#if HAVE_XKB_EXTENSION
+ // get effective group for key
+ int numGroups = XkbKeyNumGroups(m_xkb, keycode);
+ if (group >= numGroups) {
+ unsigned char groupInfo = XkbKeyGroupInfo(m_xkb, keycode);
+ switch (XkbOutOfRangeGroupAction(groupInfo)) {
+ case XkbClampIntoRange:
+ group = numGroups - 1;
+ break;
+
+ case XkbRedirectIntoRange:
+ group = XkbOutOfRangeGroupNumber(groupInfo);
+ if (group >= numGroups) {
+ group = 0;
+ }
+ break;
+
+ default:
+ // wrap
+ group %= numGroups;
+ break;
+ }
+ }
+#endif
+ return group;
+}
+
+UInt32
+XWindowsKeyState::getGroupFromState(unsigned int state) const
+{
+#if HAVE_XKB_EXTENSION
+ if (m_xkb != NULL) {
+ return XkbGroupForCoreState(state);
+ }
+#endif
+ return 0;
+}
diff --git a/src/lib/platform/XWindowsKeyState.h b/src/lib/platform/XWindowsKeyState.h
new file mode 100644
index 0000000..f3c0a1e
--- /dev/null
+++ b/src/lib/platform/XWindowsKeyState.h
@@ -0,0 +1,174 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2003 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 "barrier/KeyState.h"
+#include "common/stdmap.h"
+#include "common/stdvector.h"
+
+#if X_DISPLAY_MISSING
+# error X11 is required to build barrier
+#else
+# include <X11/Xlib.h>
+# if HAVE_X11_EXTENSIONS_XTEST_H
+# include <X11/extensions/XTest.h>
+# else
+# error The XTest extension is required to build barrier
+# endif
+# if HAVE_XKB_EXTENSION
+# include <X11/extensions/XKBstr.h>
+# endif
+#endif
+
+class IEventQueue;
+
+//! X Windows key state
+/*!
+A key state for X Windows.
+*/
+class XWindowsKeyState : public KeyState {
+public:
+ typedef std::vector<int> KeycodeList;
+ enum {
+ kGroupPoll = -1,
+ kGroupPollAndSet = -2
+ };
+
+ XWindowsKeyState(Display*, bool useXKB, IEventQueue* events);
+ XWindowsKeyState(Display*, bool useXKB,
+ IEventQueue* events, barrier::KeyMap& keyMap);
+ ~XWindowsKeyState();
+
+ //! @name modifiers
+ //@{
+
+ //! Set active group
+ /*!
+ Sets the active group to \p group. This is the group returned by
+ \c pollActiveGroup(). If \p group is \c kGroupPoll then
+ \c pollActiveGroup() will really poll, but that's a slow operation
+ on X11. If \p group is \c kGroupPollAndSet then this will poll the
+ active group now and use it for future calls to \c pollActiveGroup().
+ */
+ void setActiveGroup(SInt32 group);
+
+ //! Set the auto-repeat state
+ /*!
+ Sets the auto-repeat state.
+ */
+ void setAutoRepeat(const XKeyboardState&);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Convert X modifier mask to barrier mask
+ /*!
+ Returns the barrier modifier mask corresponding to the X modifier
+ mask in \p state.
+ */
+ KeyModifierMask mapModifiersFromX(unsigned int state) const;
+
+ //! Convert barrier modifier mask to X mask
+ /*!
+ Converts the barrier modifier mask to the corresponding X modifier
+ mask. Returns \c true if successful and \c false if any modifier
+ could not be converted.
+ */
+ bool mapModifiersToX(KeyModifierMask, unsigned int&) const;
+
+ //! Convert barrier key to all corresponding X keycodes
+ /*!
+ Converts the barrier key \p key to all of the keycodes that map to
+ that key.
+ */
+ void mapKeyToKeycodes(KeyID key,
+ KeycodeList& keycodes) const;
+
+ //@}
+
+ // IKeyState overrides
+ virtual bool fakeCtrlAltDel();
+ virtual KeyModifierMask
+ pollActiveModifiers() const;
+ virtual SInt32 pollActiveGroup() const;
+ virtual void pollPressedKeys(KeyButtonSet& pressedKeys) const;
+
+protected:
+ // KeyState overrides
+ virtual void getKeyMap(barrier::KeyMap& keyMap);
+ virtual void fakeKey(const Keystroke& keystroke);
+
+private:
+ void init(Display* display, bool useXKB);
+ void updateKeysymMap(barrier::KeyMap&);
+ void updateKeysymMapXKB(barrier::KeyMap&);
+ bool hasModifiersXKB() const;
+ int getEffectiveGroup(KeyCode, int group) const;
+ UInt32 getGroupFromState(unsigned int state) const;
+
+ static void remapKeyModifiers(KeyID, SInt32,
+ barrier::KeyMap::KeyItem&, void*);
+
+private:
+ struct XKBModifierInfo {
+ public:
+ unsigned char m_level;
+ UInt32 m_mask;
+ bool m_lock;
+ };
+
+#ifdef TEST_ENV
+public: // yuck
+#endif
+ typedef std::vector<KeyModifierMask> KeyModifierMaskList;
+
+private:
+ typedef std::map<KeyModifierMask, unsigned int> KeyModifierToXMask;
+ typedef std::multimap<KeyID, KeyCode> KeyToKeyCodeMap;
+ typedef std::map<KeyCode, unsigned int> NonXKBModifierMap;
+ typedef std::map<UInt32, XKBModifierInfo> XKBModifierMap;
+
+ Display* m_display;
+#if HAVE_XKB_EXTENSION
+ XkbDescPtr m_xkb;
+#endif
+ SInt32 m_group;
+ XKBModifierMap m_lastGoodXKBModifiers;
+ NonXKBModifierMap m_lastGoodNonXKBModifiers;
+
+ // X modifier (bit number) to barrier modifier (mask) mapping
+ KeyModifierMaskList m_modifierFromX;
+
+ // barrier modifier (mask) to X modifier (mask)
+ KeyModifierToXMask m_modifierToX;
+
+ // map KeyID to all keycodes that can synthesize that KeyID
+ KeyToKeyCodeMap m_keyCodeFromKey;
+
+ // autorepeat state
+ XKeyboardState m_keyboardState;
+
+#ifdef TEST_ENV
+public:
+ SInt32 group() const { return m_group; }
+ void group(const SInt32& group) { m_group = group; }
+ KeyModifierMaskList modifierFromX() const { return m_modifierFromX; }
+#endif
+};
diff --git a/src/lib/platform/XWindowsScreen.cpp b/src/lib/platform/XWindowsScreen.cpp
new file mode 100644
index 0000000..581c911
--- /dev/null
+++ b/src/lib/platform/XWindowsScreen.cpp
@@ -0,0 +1,2096 @@
+/*
+ * barrier -- 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 "platform/XWindowsScreen.h"
+
+#include "platform/XWindowsClipboard.h"
+#include "platform/XWindowsEventQueueBuffer.h"
+#include "platform/XWindowsKeyState.h"
+#include "platform/XWindowsScreenSaver.h"
+#include "platform/XWindowsUtil.h"
+#include "barrier/Clipboard.h"
+#include "barrier/KeyMap.h"
+#include "barrier/XScreen.h"
+#include "arch/XArch.h"
+#include "arch/Arch.h"
+#include "base/Log.h"
+#include "base/Stopwatch.h"
+#include "base/String.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+
+#include <cstring>
+#include <cstdlib>
+#include <algorithm>
+#if X_DISPLAY_MISSING
+# error X11 is required to build barrier
+#else
+# include <X11/X.h>
+# include <X11/Xutil.h>
+# define XK_MISCELLANY
+# define XK_XKB_KEYS
+# include <X11/keysymdef.h>
+# if HAVE_X11_EXTENSIONS_DPMS_H
+ extern "C" {
+# include <X11/extensions/dpms.h>
+ }
+# endif
+# if HAVE_X11_EXTENSIONS_XTEST_H
+# include <X11/extensions/XTest.h>
+# else
+# error The XTest extension is required to build barrier
+# endif
+# if HAVE_X11_EXTENSIONS_XINERAMA_H
+ // Xinerama.h may lack extern "C" for inclusion by C++
+ extern "C" {
+# include <X11/extensions/Xinerama.h>
+ }
+# endif
+# if HAVE_X11_EXTENSIONS_XRANDR_H
+# include <X11/extensions/Xrandr.h>
+# endif
+# if HAVE_XKB_EXTENSION
+# include <X11/XKBlib.h>
+# endif
+# ifdef HAVE_XI2
+# include <X11/extensions/XInput2.h>
+# endif
+#endif
+
+static int xi_opcode;
+
+//
+// XWindowsScreen
+//
+
+// NOTE -- the X display is shared among several objects but is owned
+// by the XWindowsScreen. Xlib is not reentrant so we must ensure
+// that no two objects can simultaneously call Xlib with the display.
+// this is easy since we only make X11 calls from the main thread.
+// we must also ensure that these objects do not use the display in
+// their destructors or, if they do, we can tell them not to. This
+// is to handle unexpected disconnection of the X display, when any
+// call on the display is invalid. In that situation we discard the
+// display and the X11 event queue buffer, ignore any calls that try
+// to use the display, and wait to be destroyed.
+
+XWindowsScreen* XWindowsScreen::s_screen = NULL;
+
+XWindowsScreen::XWindowsScreen(
+ const char* displayName,
+ bool isPrimary,
+ bool disableXInitThreads,
+ int mouseScrollDelta,
+ IEventQueue* events) :
+ m_isPrimary(isPrimary),
+ m_mouseScrollDelta(mouseScrollDelta),
+ m_display(NULL),
+ m_root(None),
+ m_window(None),
+ m_isOnScreen(m_isPrimary),
+ m_x(0), m_y(0),
+ m_w(0), m_h(0),
+ m_xCenter(0), m_yCenter(0),
+ m_xCursor(0), m_yCursor(0),
+ m_keyState(NULL),
+ m_lastFocus(None),
+ m_lastFocusRevert(RevertToNone),
+ m_im(NULL),
+ m_ic(NULL),
+ m_lastKeycode(0),
+ m_sequenceNumber(0),
+ m_screensaver(NULL),
+ m_screensaverNotify(false),
+ m_xtestIsXineramaUnaware(true),
+ m_preserveFocus(false),
+ m_xkb(false),
+ m_xi2detected(false),
+ m_xrandr(false),
+ m_events(events),
+ PlatformScreen(events)
+{
+ assert(s_screen == NULL);
+
+ if (mouseScrollDelta==0) m_mouseScrollDelta=120;
+ s_screen = this;
+
+ if (!disableXInitThreads) {
+ // initializes Xlib support for concurrent threads.
+ if (XInitThreads() == 0)
+ throw XArch("XInitThreads() returned zero");
+ } else {
+ LOG((CLOG_DEBUG "skipping XInitThreads()"));
+ }
+
+ // set the X I/O error handler so we catch the display disconnecting
+ XSetIOErrorHandler(&XWindowsScreen::ioErrorHandler);
+
+ try {
+ m_display = openDisplay(displayName);
+ m_root = DefaultRootWindow(m_display);
+ saveShape();
+ m_window = openWindow();
+ m_screensaver = new XWindowsScreenSaver(m_display,
+ m_window, getEventTarget(), events);
+ m_keyState = new XWindowsKeyState(m_display, m_xkb, events, m_keyMap);
+ LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_xinerama ? "(xinerama)" : ""));
+ LOG((CLOG_DEBUG "window is 0x%08x", m_window));
+ }
+ catch (...) {
+ if (m_display != NULL) {
+ XCloseDisplay(m_display);
+ }
+ throw;
+ }
+
+ // primary/secondary screen only initialization
+ if (m_isPrimary) {
+#ifdef HAVE_XI2
+ m_xi2detected = detectXI2();
+ if (m_xi2detected) {
+ selectXIRawMotion();
+ } else
+#endif
+ {
+ // start watching for events on other windows
+ selectEvents(m_root);
+ }
+
+ // prepare to use input methods
+ openIM();
+ }
+ else {
+ // become impervious to server grabs
+ XTestGrabControl(m_display, True);
+ }
+
+ // initialize the clipboards
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ m_clipboard[id] = new XWindowsClipboard(m_display, m_window, id);
+ }
+
+ // install event handlers
+ m_events->adoptHandler(Event::kSystem, m_events->getSystemTarget(),
+ new TMethodEventJob<XWindowsScreen>(this,
+ &XWindowsScreen::handleSystemEvent));
+
+ // install the platform event queue
+ m_events->adoptBuffer(new XWindowsEventQueueBuffer(
+ m_display, m_window, m_events));
+}
+
+XWindowsScreen::~XWindowsScreen()
+{
+ assert(s_screen != NULL);
+ assert(m_display != NULL);
+
+ m_events->adoptBuffer(NULL);
+ m_events->removeHandler(Event::kSystem, m_events->getSystemTarget());
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ delete m_clipboard[id];
+ }
+ delete m_keyState;
+ delete m_screensaver;
+ m_keyState = NULL;
+ m_screensaver = NULL;
+ if (m_display != NULL) {
+ // FIXME -- is it safe to clean up the IC and IM without a display?
+ if (m_ic != NULL) {
+ XDestroyIC(m_ic);
+ }
+ if (m_im != NULL) {
+ XCloseIM(m_im);
+ }
+ XDestroyWindow(m_display, m_window);
+ XCloseDisplay(m_display);
+ }
+ XSetIOErrorHandler(NULL);
+
+ s_screen = NULL;
+}
+
+void
+XWindowsScreen::enable()
+{
+ if (!m_isPrimary) {
+ // get the keyboard control state
+ XKeyboardState keyControl;
+ XGetKeyboardControl(m_display, &keyControl);
+ m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn);
+ m_keyState->setAutoRepeat(keyControl);
+
+ // move hider window under the cursor center
+ XMoveWindow(m_display, m_window, m_xCenter, m_yCenter);
+
+ // raise and show the window
+ // FIXME -- take focus?
+ XMapRaised(m_display, m_window);
+
+ // warp the mouse to the cursor center
+ fakeMouseMove(m_xCenter, m_yCenter);
+ }
+}
+
+void
+XWindowsScreen::disable()
+{
+ // release input context focus
+ if (m_ic != NULL) {
+ XUnsetICFocus(m_ic);
+ }
+
+ // unmap the hider/grab window. this also ungrabs the mouse and
+ // keyboard if they're grabbed.
+ XUnmapWindow(m_display, m_window);
+
+ // restore auto-repeat state
+ if (!m_isPrimary && m_autoRepeat) {
+ //XAutoRepeatOn(m_display);
+ }
+}
+
+void
+XWindowsScreen::enter()
+{
+ screensaver(false);
+
+ // release input context focus
+ if (m_ic != NULL) {
+ XUnsetICFocus(m_ic);
+ }
+
+ // set the input focus to what it had been when we took it
+ if (m_lastFocus != None) {
+ // the window may not exist anymore so ignore errors
+ XWindowsUtil::ErrorLock lock(m_display);
+ XSetInputFocus(m_display, m_lastFocus, m_lastFocusRevert, CurrentTime);
+ }
+
+ #if HAVE_X11_EXTENSIONS_DPMS_H
+ // Force the DPMS to turn screen back on since we don't
+ // actually cause physical hardware input to trigger it
+ int dummy;
+ CARD16 powerlevel;
+ BOOL enabled;
+ if (DPMSQueryExtension(m_display, &dummy, &dummy) &&
+ DPMSCapable(m_display) &&
+ DPMSInfo(m_display, &powerlevel, &enabled))
+ {
+ if (enabled && powerlevel != DPMSModeOn)
+ DPMSForceLevel(m_display, DPMSModeOn);
+ }
+ #endif
+
+ // unmap the hider/grab window. this also ungrabs the mouse and
+ // keyboard if they're grabbed.
+ XUnmapWindow(m_display, m_window);
+
+/* maybe call this if entering for the screensaver
+ // set keyboard focus to root window. the screensaver should then
+ // pick up key events for when the user enters a password to unlock.
+ XSetInputFocus(m_display, PointerRoot, PointerRoot, CurrentTime);
+*/
+
+ if (!m_isPrimary) {
+ // get the keyboard control state
+ XKeyboardState keyControl;
+ XGetKeyboardControl(m_display, &keyControl);
+ m_autoRepeat = (keyControl.global_auto_repeat == AutoRepeatModeOn);
+ m_keyState->setAutoRepeat(keyControl);
+
+ // turn off auto-repeat. we do this so fake key press events don't
+ // cause the local server to generate their own auto-repeats of
+ // those keys.
+ //XAutoRepeatOff(m_display);
+ }
+
+ // now on screen
+ m_isOnScreen = true;
+}
+
+bool
+XWindowsScreen::leave()
+{
+ if (!m_isPrimary) {
+ // restore the previous keyboard auto-repeat state. if the user
+ // changed the auto-repeat configuration while on the client then
+ // that state is lost. that's because we can't get notified by
+ // the X server when the auto-repeat configuration is changed so
+ // we can't track the desired configuration.
+ if (m_autoRepeat) {
+ //XAutoRepeatOn(m_display);
+ }
+
+ // move hider window under the cursor center
+ XMoveWindow(m_display, m_window, m_xCenter, m_yCenter);
+ }
+
+ // raise and show the window
+ XMapRaised(m_display, m_window);
+
+ // grab the mouse and keyboard, if primary and possible
+ if (m_isPrimary && !grabMouseAndKeyboard()) {
+ XUnmapWindow(m_display, m_window);
+ return false;
+ }
+
+ // save current focus
+ XGetInputFocus(m_display, &m_lastFocus, &m_lastFocusRevert);
+
+ // take focus
+ if (m_isPrimary || !m_preserveFocus) {
+ XSetInputFocus(m_display, m_window, RevertToPointerRoot, CurrentTime);
+ }
+
+ // now warp the mouse. we warp after showing the window so we're
+ // guaranteed to get the mouse leave event and to prevent the
+ // keyboard focus from changing under point-to-focus policies.
+ if (m_isPrimary) {
+ warpCursor(m_xCenter, m_yCenter);
+ }
+ else {
+ fakeMouseMove(m_xCenter, m_yCenter);
+ }
+
+ // set input context focus to our window
+ if (m_ic != NULL) {
+ XmbResetIC(m_ic);
+ XSetICFocus(m_ic);
+ m_filtered.clear();
+ }
+
+ // now off screen
+ m_isOnScreen = false;
+
+ return true;
+}
+
+bool
+XWindowsScreen::setClipboard(ClipboardID id, const IClipboard* clipboard)
+{
+ // fail if we don't have the requested clipboard
+ if (m_clipboard[id] == NULL) {
+ return false;
+ }
+
+ // get the actual time. ICCCM does not allow CurrentTime.
+ Time timestamp = XWindowsUtil::getCurrentTime(
+ m_display, m_clipboard[id]->getWindow());
+
+ if (clipboard != NULL) {
+ // save clipboard data
+ return Clipboard::copy(m_clipboard[id], clipboard, timestamp);
+ }
+ else {
+ // assert clipboard ownership
+ if (!m_clipboard[id]->open(timestamp)) {
+ return false;
+ }
+ m_clipboard[id]->empty();
+ m_clipboard[id]->close();
+ return true;
+ }
+}
+
+void
+XWindowsScreen::checkClipboards()
+{
+ // do nothing, we're always up to date
+}
+
+void
+XWindowsScreen::openScreensaver(bool notify)
+{
+ m_screensaverNotify = notify;
+ if (!m_screensaverNotify) {
+ m_screensaver->disable();
+ }
+}
+
+void
+XWindowsScreen::closeScreensaver()
+{
+ if (!m_screensaverNotify) {
+ m_screensaver->enable();
+ }
+}
+
+void
+XWindowsScreen::screensaver(bool activate)
+{
+ if (activate) {
+ m_screensaver->activate();
+ }
+ else {
+ m_screensaver->deactivate();
+ }
+}
+
+void
+XWindowsScreen::resetOptions()
+{
+ m_xtestIsXineramaUnaware = true;
+ m_preserveFocus = false;
+}
+
+void
+XWindowsScreen::setOptions(const OptionsList& options)
+{
+ for (UInt32 i = 0, n = options.size(); i < n; i += 2) {
+ if (options[i] == kOptionXTestXineramaUnaware) {
+ m_xtestIsXineramaUnaware = (options[i + 1] != 0);
+ LOG((CLOG_DEBUG1 "XTest is Xinerama unaware %s", m_xtestIsXineramaUnaware ? "true" : "false"));
+ }
+ else if (options[i] == kOptionScreenPreserveFocus) {
+ m_preserveFocus = (options[i + 1] != 0);
+ LOG((CLOG_DEBUG1 "Preserve Focus = %s", m_preserveFocus ? "true" : "false"));
+ }
+ }
+}
+
+void
+XWindowsScreen::setSequenceNumber(UInt32 seqNum)
+{
+ m_sequenceNumber = seqNum;
+}
+
+bool
+XWindowsScreen::isPrimary() const
+{
+ return m_isPrimary;
+}
+
+void*
+XWindowsScreen::getEventTarget() const
+{
+ return const_cast<XWindowsScreen*>(this);
+}
+
+bool
+XWindowsScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const
+{
+ assert(clipboard != NULL);
+
+ // fail if we don't have the requested clipboard
+ if (m_clipboard[id] == NULL) {
+ return false;
+ }
+
+ // get the actual time. ICCCM does not allow CurrentTime.
+ Time timestamp = XWindowsUtil::getCurrentTime(
+ m_display, m_clipboard[id]->getWindow());
+
+ // copy the clipboard
+ return Clipboard::copy(clipboard, m_clipboard[id], timestamp);
+}
+
+void
+XWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
+{
+ x = m_x;
+ y = m_y;
+ w = m_w;
+ h = m_h;
+}
+
+void
+XWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
+{
+ Window root, window;
+ int mx, my, xWindow, yWindow;
+ unsigned int mask;
+ if (XQueryPointer(m_display, m_root, &root, &window,
+ &mx, &my, &xWindow, &yWindow, &mask)) {
+ x = mx;
+ y = my;
+ }
+ else {
+ x = m_xCenter;
+ y = m_yCenter;
+ }
+}
+
+void
+XWindowsScreen::reconfigure(UInt32)
+{
+ // do nothing
+}
+
+void
+XWindowsScreen::warpCursor(SInt32 x, SInt32 y)
+{
+ // warp mouse
+ warpCursorNoFlush(x, y);
+
+ // remove all input events before and including warp
+ XEvent event;
+ while (XCheckMaskEvent(m_display, PointerMotionMask |
+ ButtonPressMask | ButtonReleaseMask |
+ KeyPressMask | KeyReleaseMask |
+ KeymapStateMask,
+ &event)) {
+ // do nothing
+ }
+
+ // save position as last position
+ m_xCursor = x;
+ m_yCursor = y;
+}
+
+UInt32
+XWindowsScreen::registerHotKey(KeyID key, KeyModifierMask mask)
+{
+ // only allow certain modifiers
+ if ((mask & ~(KeyModifierShift | KeyModifierControl |
+ KeyModifierAlt | KeyModifierSuper)) != 0) {
+ LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
+ return 0;
+ }
+
+ // fail if no keys
+ if (key == kKeyNone && mask == 0) {
+ return 0;
+ }
+
+ // convert to X
+ unsigned int modifiers;
+ if (!m_keyState->mapModifiersToX(mask, modifiers)) {
+ // can't map all modifiers
+ LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
+ return 0;
+ }
+ XWindowsKeyState::KeycodeList keycodes;
+ m_keyState->mapKeyToKeycodes(key, keycodes);
+ if (key != kKeyNone && keycodes.empty()) {
+ // can't map key
+ LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
+ return 0;
+ }
+
+ // choose hotkey id
+ UInt32 id;
+ if (!m_oldHotKeyIDs.empty()) {
+ id = m_oldHotKeyIDs.back();
+ m_oldHotKeyIDs.pop_back();
+ }
+ else {
+ id = m_hotKeys.size() + 1;
+ }
+ HotKeyList& hotKeys = m_hotKeys[id];
+
+ // all modifier hotkey must be treated specially. for each modifier
+ // we need to grab the modifier key in combination with all the other
+ // requested modifiers.
+ bool err = false;
+ {
+ XWindowsUtil::ErrorLock lock(m_display, &err);
+ if (key == kKeyNone) {
+ static const KeyModifierMask s_hotKeyModifiers[] = {
+ KeyModifierShift,
+ KeyModifierControl,
+ KeyModifierAlt,
+ KeyModifierMeta,
+ KeyModifierSuper
+ };
+
+ XModifierKeymap* modKeymap = XGetModifierMapping(m_display);
+ for (size_t j = 0; j < sizeof(s_hotKeyModifiers) /
+ sizeof(s_hotKeyModifiers[0]) && !err; ++j) {
+ // skip modifier if not in mask
+ if ((mask & s_hotKeyModifiers[j]) == 0) {
+ continue;
+ }
+
+ // skip with error if we can't map remaining modifiers
+ unsigned int modifiers2;
+ KeyModifierMask mask2 = (mask & ~s_hotKeyModifiers[j]);
+ if (!m_keyState->mapModifiersToX(mask2, modifiers2)) {
+ err = true;
+ continue;
+ }
+
+ // compute modifier index for modifier. there should be
+ // exactly one X modifier missing
+ int index;
+ switch (modifiers ^ modifiers2) {
+ case ShiftMask:
+ index = ShiftMapIndex;
+ break;
+
+ case LockMask:
+ index = LockMapIndex;
+ break;
+
+ case ControlMask:
+ index = ControlMapIndex;
+ break;
+
+ case Mod1Mask:
+ index = Mod1MapIndex;
+ break;
+
+ case Mod2Mask:
+ index = Mod2MapIndex;
+ break;
+
+ case Mod3Mask:
+ index = Mod3MapIndex;
+ break;
+
+ case Mod4Mask:
+ index = Mod4MapIndex;
+ break;
+
+ case Mod5Mask:
+ index = Mod5MapIndex;
+ break;
+
+ default:
+ err = true;
+ continue;
+ }
+
+ // grab each key for the modifier
+ const KeyCode* modifiermap =
+ modKeymap->modifiermap + index * modKeymap->max_keypermod;
+ for (int k = 0; k < modKeymap->max_keypermod && !err; ++k) {
+ KeyCode code = modifiermap[k];
+ if (modifiermap[k] != 0) {
+ XGrabKey(m_display, code, modifiers2, m_root,
+ False, GrabModeAsync, GrabModeAsync);
+ if (!err) {
+ hotKeys.push_back(std::make_pair(code, modifiers2));
+ m_hotKeyToIDMap[HotKeyItem(code, modifiers2)] = id;
+ }
+ }
+ }
+ }
+ XFreeModifiermap(modKeymap);
+ }
+
+ // a non-modifier key must be insensitive to CapsLock, NumLock and
+ // ScrollLock, so we have to grab the key with every combination of
+ // those.
+ else {
+ // collect available toggle modifiers
+ unsigned int modifier;
+ unsigned int toggleModifiers[3];
+ size_t numToggleModifiers = 0;
+ if (m_keyState->mapModifiersToX(KeyModifierCapsLock, modifier)) {
+ toggleModifiers[numToggleModifiers++] = modifier;
+ }
+ if (m_keyState->mapModifiersToX(KeyModifierNumLock, modifier)) {
+ toggleModifiers[numToggleModifiers++] = modifier;
+ }
+ if (m_keyState->mapModifiersToX(KeyModifierScrollLock, modifier)) {
+ toggleModifiers[numToggleModifiers++] = modifier;
+ }
+
+
+ for (XWindowsKeyState::KeycodeList::iterator j = keycodes.begin();
+ j != keycodes.end() && !err; ++j) {
+ for (size_t i = 0; i < (1u << numToggleModifiers); ++i) {
+ // add toggle modifiers for index i
+ unsigned int tmpModifiers = modifiers;
+ if ((i & 1) != 0) {
+ tmpModifiers |= toggleModifiers[0];
+ }
+ if ((i & 2) != 0) {
+ tmpModifiers |= toggleModifiers[1];
+ }
+ if ((i & 4) != 0) {
+ tmpModifiers |= toggleModifiers[2];
+ }
+
+ // add grab
+ XGrabKey(m_display, *j, tmpModifiers, m_root,
+ False, GrabModeAsync, GrabModeAsync);
+ if (!err) {
+ hotKeys.push_back(std::make_pair(*j, tmpModifiers));
+ m_hotKeyToIDMap[HotKeyItem(*j, tmpModifiers)] = id;
+ }
+ }
+ }
+ }
+ }
+
+ if (err) {
+ // if any failed then unregister any we did get
+ for (HotKeyList::iterator j = hotKeys.begin();
+ j != hotKeys.end(); ++j) {
+ XUngrabKey(m_display, j->first, j->second, m_root);
+ m_hotKeyToIDMap.erase(HotKeyItem(j->first, j->second));
+ }
+
+ m_oldHotKeyIDs.push_back(id);
+ m_hotKeys.erase(id);
+ LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask));
+ return 0;
+ }
+
+ LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", barrier::KeyMap::formatKey(key, mask).c_str(), key, mask, id));
+ return id;
+}
+
+void
+XWindowsScreen::unregisterHotKey(UInt32 id)
+{
+ // look up hotkey
+ HotKeyMap::iterator i = m_hotKeys.find(id);
+ if (i == m_hotKeys.end()) {
+ return;
+ }
+
+ // unregister with OS
+ bool err = false;
+ {
+ XWindowsUtil::ErrorLock lock(m_display, &err);
+ HotKeyList& hotKeys = i->second;
+ for (HotKeyList::iterator j = hotKeys.begin();
+ j != hotKeys.end(); ++j) {
+ XUngrabKey(m_display, j->first, j->second, m_root);
+ m_hotKeyToIDMap.erase(HotKeyItem(j->first, j->second));
+ }
+ }
+ if (err) {
+ LOG((CLOG_WARN "failed to unregister hotkey id=%d", id));
+ }
+ else {
+ LOG((CLOG_DEBUG "unregistered hotkey id=%d", id));
+ }
+
+ // discard hot key from map and record old id for reuse
+ m_hotKeys.erase(i);
+ m_oldHotKeyIDs.push_back(id);
+}
+
+void
+XWindowsScreen::fakeInputBegin()
+{
+ // FIXME -- not implemented
+}
+
+void
+XWindowsScreen::fakeInputEnd()
+{
+ // FIXME -- not implemented
+}
+
+SInt32
+XWindowsScreen::getJumpZoneSize() const
+{
+ return 1;
+}
+
+bool
+XWindowsScreen::isAnyMouseButtonDown(UInt32& buttonID) const
+{
+ // query the pointer to get the button state
+ Window root, window;
+ int xRoot, yRoot, xWindow, yWindow;
+ unsigned int state;
+ if (XQueryPointer(m_display, m_root, &root, &window,
+ &xRoot, &yRoot, &xWindow, &yWindow, &state)) {
+ return ((state & (Button1Mask | Button2Mask | Button3Mask |
+ Button4Mask | Button5Mask)) != 0);
+ }
+
+ return false;
+}
+
+void
+XWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const
+{
+ x = m_xCenter;
+ y = m_yCenter;
+}
+
+void
+XWindowsScreen::fakeMouseButton(ButtonID button, bool press)
+{
+ const unsigned int xButton = mapButtonToX(button);
+ if (xButton > 0 && xButton < 11) {
+ XTestFakeButtonEvent(m_display, xButton,
+ press ? True : False, CurrentTime);
+ XFlush(m_display);
+ }
+}
+
+void
+XWindowsScreen::fakeMouseMove(SInt32 x, SInt32 y)
+{
+ if (m_xinerama && m_xtestIsXineramaUnaware) {
+ XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
+ }
+ else {
+ XTestFakeMotionEvent(m_display, DefaultScreen(m_display),
+ x, y, CurrentTime);
+ }
+ XFlush(m_display);
+}
+
+void
+XWindowsScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
+{
+ // FIXME -- ignore xinerama for now
+ if (false && m_xinerama && m_xtestIsXineramaUnaware) {
+// XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
+ }
+ else {
+ XTestFakeRelativeMotionEvent(m_display, dx, dy, CurrentTime);
+ }
+ XFlush(m_display);
+}
+
+void
+XWindowsScreen::fakeMouseWheel(SInt32, SInt32 yDelta) const
+{
+ // XXX -- support x-axis scrolling
+ if (yDelta == 0) {
+ return;
+ }
+
+ // choose button depending on rotation direction
+ const unsigned int xButton = mapButtonToX(static_cast<ButtonID>(
+ (yDelta >= 0) ? -1 : -2));
+ if (xButton == 0) {
+ // If we get here, then the XServer does not support the scroll
+ // wheel buttons, so send PageUp/PageDown keystrokes instead.
+ // Patch by Tom Chadwick.
+ KeyCode keycode = 0;
+ if (yDelta >= 0) {
+ keycode = XKeysymToKeycode(m_display, XK_Page_Up);
+ }
+ else {
+ keycode = XKeysymToKeycode(m_display, XK_Page_Down);
+ }
+ if (keycode != 0) {
+ XTestFakeKeyEvent(m_display, keycode, True, CurrentTime);
+ XTestFakeKeyEvent(m_display, keycode, False, CurrentTime);
+ }
+ return;
+ }
+
+ // now use absolute value of delta
+ if (yDelta < 0) {
+ yDelta = -yDelta;
+ }
+
+ if (yDelta < m_mouseScrollDelta) {
+ LOG((CLOG_WARN "Wheel scroll delta (%d) smaller than threshold (%d)", yDelta, m_mouseScrollDelta));
+ }
+
+ // send as many clicks as necessary
+ for (; yDelta >= m_mouseScrollDelta; yDelta -= m_mouseScrollDelta) {
+ XTestFakeButtonEvent(m_display, xButton, True, CurrentTime);
+ XTestFakeButtonEvent(m_display, xButton, False, CurrentTime);
+ }
+ XFlush(m_display);
+}
+
+Display*
+XWindowsScreen::openDisplay(const char* displayName)
+{
+ // get the DISPLAY
+ if (displayName == NULL) {
+ displayName = getenv("DISPLAY");
+ if (displayName == NULL) {
+ displayName = ":0.0";
+ }
+ }
+
+ // open the display
+ LOG((CLOG_DEBUG "XOpenDisplay(\"%s\")", displayName));
+ Display* display = XOpenDisplay(displayName);
+ if (display == NULL) {
+ throw XScreenUnavailable(60.0);
+ }
+
+ // verify the availability of the XTest extension
+ if (!m_isPrimary) {
+ int majorOpcode, firstEvent, firstError;
+ if (!XQueryExtension(display, XTestExtensionName,
+ &majorOpcode, &firstEvent, &firstError)) {
+ LOG((CLOG_ERR "XTEST extension not available"));
+ XCloseDisplay(display);
+ throw XScreenOpenFailure();
+ }
+ }
+
+#if HAVE_XKB_EXTENSION
+ {
+ m_xkb = false;
+ int major = XkbMajorVersion, minor = XkbMinorVersion;
+ if (XkbLibraryVersion(&major, &minor)) {
+ int opcode, firstError;
+ if (XkbQueryExtension(display, &opcode, &m_xkbEventBase,
+ &firstError, &major, &minor)) {
+ m_xkb = true;
+ XkbSelectEvents(display, XkbUseCoreKbd,
+ XkbMapNotifyMask, XkbMapNotifyMask);
+ XkbSelectEventDetails(display, XkbUseCoreKbd,
+ XkbStateNotifyMask,
+ XkbGroupStateMask, XkbGroupStateMask);
+ }
+ }
+ }
+#endif
+
+#if HAVE_X11_EXTENSIONS_XRANDR_H
+ // query for XRandR extension
+ int dummyError;
+ m_xrandr = XRRQueryExtension(display, &m_xrandrEventBase, &dummyError);
+ if (m_xrandr) {
+ // enable XRRScreenChangeNotifyEvent
+ XRRSelectInput(display, DefaultRootWindow(display), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask);
+ }
+#endif
+
+ return display;
+}
+
+void
+XWindowsScreen::saveShape()
+{
+ // get shape of default screen
+ m_x = 0;
+ m_y = 0;
+
+ m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display));
+ m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display));
+
+ // get center of default screen
+ m_xCenter = m_x + (m_w >> 1);
+ m_yCenter = m_y + (m_h >> 1);
+
+ // check if xinerama is enabled and there is more than one screen.
+ // get center of first Xinerama screen. Xinerama appears to have
+ // a bug when XWarpPointer() is used in combination with
+ // XGrabPointer(). in that case, the warp is successful but the
+ // next pointer motion warps the pointer again, apparently to
+ // constrain it to some unknown region, possibly the region from
+ // 0,0 to Wm,Hm where Wm (Hm) is the minimum width (height) over
+ // all physical screens. this warp only seems to happen if the
+ // pointer wasn't in that region before the XWarpPointer(). the
+ // second (unexpected) warp causes barrier to think the pointer
+ // has been moved when it hasn't. to work around the problem,
+ // we warp the pointer to the center of the first physical
+ // screen instead of the logical screen.
+ m_xinerama = false;
+#if HAVE_X11_EXTENSIONS_XINERAMA_H
+ int eventBase, errorBase;
+ if (XineramaQueryExtension(m_display, &eventBase, &errorBase) &&
+ XineramaIsActive(m_display)) {
+ int numScreens;
+ XineramaScreenInfo* screens;
+ screens = XineramaQueryScreens(m_display, &numScreens);
+ if (screens != NULL) {
+ if (numScreens > 1) {
+ m_xinerama = true;
+ m_xCenter = screens[0].x_org + (screens[0].width >> 1);
+ m_yCenter = screens[0].y_org + (screens[0].height >> 1);
+ }
+ XFree(screens);
+ }
+ }
+#endif
+}
+
+Window
+XWindowsScreen::openWindow() const
+{
+ // default window attributes. we don't want the window manager
+ // messing with our window and we don't want the cursor to be
+ // visible inside the window.
+ XSetWindowAttributes attr;
+ attr.do_not_propagate_mask = 0;
+ attr.override_redirect = True;
+ attr.cursor = createBlankCursor();
+
+ // adjust attributes and get size and shape
+ SInt32 x, y, w, h;
+ if (m_isPrimary) {
+ // grab window attributes. this window is used to capture user
+ // input when the user is focused on another client. it covers
+ // the whole screen.
+ attr.event_mask = PointerMotionMask |
+ ButtonPressMask | ButtonReleaseMask |
+ KeyPressMask | KeyReleaseMask |
+ KeymapStateMask | PropertyChangeMask;
+ x = m_x;
+ y = m_y;
+ w = m_w;
+ h = m_h;
+ }
+ else {
+ // cursor hider window attributes. this window is used to hide the
+ // cursor when it's not on the screen. the window is hidden as soon
+ // as the cursor enters the screen or the display's real mouse is
+ // moved. we'll reposition the window as necessary so its
+ // position here doesn't matter. it only needs to be 1x1 because
+ // it only needs to contain the cursor's hotspot.
+ attr.event_mask = LeaveWindowMask;
+ x = 0;
+ y = 0;
+ w = 1;
+ h = 1;
+ }
+
+ // create and return the window
+ Window window = XCreateWindow(m_display, m_root, x, y, w, h, 0, 0,
+ InputOnly, CopyFromParent,
+ CWDontPropagate | CWEventMask |
+ CWOverrideRedirect | CWCursor,
+ &attr);
+ if (window == None) {
+ throw XScreenOpenFailure();
+ }
+ return window;
+}
+
+void
+XWindowsScreen::openIM()
+{
+ // open the input methods
+ XIM im = XOpenIM(m_display, NULL, NULL, NULL);
+ if (im == NULL) {
+ LOG((CLOG_INFO "no support for IM"));
+ return;
+ }
+
+ // find the appropriate style. barrier supports XIMPreeditNothing
+ // only at the moment.
+ XIMStyles* styles;
+ if (XGetIMValues(im, XNQueryInputStyle, &styles, NULL) != NULL ||
+ styles == NULL) {
+ LOG((CLOG_WARN "cannot get IM styles"));
+ XCloseIM(im);
+ return;
+ }
+ XIMStyle style = 0;
+ for (unsigned short i = 0; i < styles->count_styles; ++i) {
+ style = styles->supported_styles[i];
+ if ((style & XIMPreeditNothing) != 0) {
+ if ((style & (XIMStatusNothing | XIMStatusNone)) != 0) {
+ break;
+ }
+ }
+ }
+ XFree(styles);
+ if (style == 0) {
+ LOG((CLOG_INFO "no supported IM styles"));
+ XCloseIM(im);
+ return;
+ }
+
+ // create an input context for the style and tell it about our window
+ XIC ic = XCreateIC(im, XNInputStyle, style, XNClientWindow, m_window, NULL);
+ if (ic == NULL) {
+ LOG((CLOG_WARN "cannot create IC"));
+ XCloseIM(im);
+ return;
+ }
+
+ // find out the events we must select for and do so
+ unsigned long mask;
+ if (XGetICValues(ic, XNFilterEvents, &mask, NULL) != NULL) {
+ LOG((CLOG_WARN "cannot get IC filter events"));
+ XDestroyIC(ic);
+ XCloseIM(im);
+ return;
+ }
+
+ // we have IM
+ m_im = im;
+ m_ic = ic;
+ m_lastKeycode = 0;
+
+ // select events on our window that IM requires
+ XWindowAttributes attr;
+ XGetWindowAttributes(m_display, m_window, &attr);
+ XSelectInput(m_display, m_window, attr.your_event_mask | mask);
+}
+
+void
+XWindowsScreen::sendEvent(Event::Type type, void* data)
+{
+ m_events->addEvent(Event(type, getEventTarget(), data));
+}
+
+void
+XWindowsScreen::sendClipboardEvent(Event::Type type, ClipboardID id)
+{
+ ClipboardInfo* info = (ClipboardInfo*)malloc(sizeof(ClipboardInfo));
+ info->m_id = id;
+ info->m_sequenceNumber = m_sequenceNumber;
+ sendEvent(type, info);
+}
+
+IKeyState*
+XWindowsScreen::getKeyState() const
+{
+ return m_keyState;
+}
+
+Bool
+XWindowsScreen::findKeyEvent(Display*, XEvent* xevent, XPointer arg)
+{
+ KeyEventFilter* filter = reinterpret_cast<KeyEventFilter*>(arg);
+ return (xevent->type == filter->m_event &&
+ xevent->xkey.window == filter->m_window &&
+ xevent->xkey.time == filter->m_time &&
+ xevent->xkey.keycode == filter->m_keycode) ? True : False;
+}
+
+void
+XWindowsScreen::handleSystemEvent(const Event& event, void*)
+{
+ XEvent* xevent = static_cast<XEvent*>(event.getData());
+ assert(xevent != NULL);
+
+ // update key state
+ bool isRepeat = false;
+ if (m_isPrimary) {
+ if (xevent->type == KeyRelease) {
+ // check if this is a key repeat by getting the next
+ // KeyPress event that has the same key and time as
+ // this release event, if any. first prepare the
+ // filter info.
+ KeyEventFilter filter;
+ filter.m_event = KeyPress;
+ filter.m_window = xevent->xkey.window;
+ filter.m_time = xevent->xkey.time;
+ filter.m_keycode = xevent->xkey.keycode;
+ XEvent xevent2;
+ isRepeat = (XCheckIfEvent(m_display, &xevent2,
+ &XWindowsScreen::findKeyEvent,
+ (XPointer)&filter) == True);
+ }
+
+ if (xevent->type == KeyPress || xevent->type == KeyRelease) {
+ if (xevent->xkey.window == m_root) {
+ // this is a hot key
+ onHotKey(xevent->xkey, isRepeat);
+ return;
+ }
+ else if (!m_isOnScreen) {
+ // this might be a hot key
+ if (onHotKey(xevent->xkey, isRepeat)) {
+ return;
+ }
+ }
+
+ bool down = (isRepeat || xevent->type == KeyPress);
+ KeyModifierMask state =
+ m_keyState->mapModifiersFromX(xevent->xkey.state);
+ m_keyState->onKey(xevent->xkey.keycode, down, state);
+ }
+ }
+
+ // let input methods try to handle event first
+ if (m_ic != NULL) {
+ // XFilterEvent() may eat the event and generate a new KeyPress
+ // event with a keycode of 0 because there isn't an actual key
+ // associated with the keysym. but the KeyRelease may pass
+ // through XFilterEvent() and keep its keycode. this means
+ // there's a mismatch between KeyPress and KeyRelease keycodes.
+ // since we use the keycode on the client to detect when a key
+ // is released this won't do. so we remember the keycode on
+ // the most recent KeyPress (and clear it on a matching
+ // KeyRelease) so we have a keycode for a synthesized KeyPress.
+ if (xevent->type == KeyPress && xevent->xkey.keycode != 0) {
+ m_lastKeycode = xevent->xkey.keycode;
+ }
+ else if (xevent->type == KeyRelease &&
+ xevent->xkey.keycode == m_lastKeycode) {
+ m_lastKeycode = 0;
+ }
+
+ // now filter the event
+ if (XFilterEvent(xevent, DefaultRootWindow(m_display))) {
+ if (xevent->type == KeyPress) {
+ // add filtered presses to the filtered list
+ m_filtered.insert(m_lastKeycode);
+ }
+ return;
+ }
+
+ // discard matching key releases for key presses that were
+ // filtered and remove them from our filtered list.
+ else if (xevent->type == KeyRelease &&
+ m_filtered.count(xevent->xkey.keycode) > 0) {
+ m_filtered.erase(xevent->xkey.keycode);
+ return;
+ }
+ }
+
+ // let screen saver have a go
+ if (m_screensaver->handleXEvent(xevent)) {
+ // screen saver handled it
+ return;
+ }
+
+#ifdef HAVE_XI2
+ if (m_xi2detected) {
+ // Process RawMotion
+ XGenericEventCookie *cookie = (XGenericEventCookie*)&xevent->xcookie;
+ if (XGetEventData(m_display, cookie) &&
+ cookie->type == GenericEvent &&
+ cookie->extension == xi_opcode) {
+ if (cookie->evtype == XI_RawMotion) {
+ // Get current pointer's position
+ Window root, child;
+ XMotionEvent xmotion;
+ xmotion.type = MotionNotify;
+ xmotion.send_event = False; // Raw motion
+ xmotion.display = m_display;
+ xmotion.window = m_window;
+ /* xmotion's time, state and is_hint are not used */
+ unsigned int msk;
+ xmotion.same_screen = XQueryPointer(
+ m_display, m_root, &xmotion.root, &xmotion.subwindow,
+ &xmotion.x_root,
+ &xmotion.y_root,
+ &xmotion.x,
+ &xmotion.y,
+ &msk);
+ onMouseMove(xmotion);
+ XFreeEventData(m_display, cookie);
+ return;
+ }
+ XFreeEventData(m_display, cookie);
+ }
+ }
+#endif
+
+ // handle the event ourself
+ switch (xevent->type) {
+ case CreateNotify:
+ if (m_isPrimary && !m_xi2detected) {
+ // select events on new window
+ selectEvents(xevent->xcreatewindow.window);
+ }
+ break;
+
+ case MappingNotify:
+ refreshKeyboard(xevent);
+ break;
+
+ case LeaveNotify:
+ if (!m_isPrimary) {
+ // mouse moved out of hider window somehow. hide the window.
+ XUnmapWindow(m_display, m_window);
+ }
+ break;
+
+ case SelectionClear:
+ {
+ // we just lost the selection. that means someone else
+ // grabbed the selection so this screen is now the
+ // selection owner. report that to the receiver.
+ ClipboardID id = getClipboardID(xevent->xselectionclear.selection);
+ if (id != kClipboardEnd) {
+ m_clipboard[id]->lost(xevent->xselectionclear.time);
+ sendClipboardEvent(m_events->forClipboard().clipboardGrabbed(), id);
+ return;
+ }
+ }
+ break;
+
+ case SelectionNotify:
+ // notification of selection transferred. we shouldn't
+ // get this here because we handle them in the selection
+ // retrieval methods. we'll just delete the property
+ // with the data (satisfying the usual ICCCM protocol).
+ if (xevent->xselection.property != None) {
+ XDeleteProperty(m_display,
+ xevent->xselection.requestor,
+ xevent->xselection.property);
+ }
+ break;
+
+ case SelectionRequest:
+ {
+ // somebody is asking for clipboard data
+ ClipboardID id = getClipboardID(
+ xevent->xselectionrequest.selection);
+ if (id != kClipboardEnd) {
+ m_clipboard[id]->addRequest(
+ xevent->xselectionrequest.owner,
+ xevent->xselectionrequest.requestor,
+ xevent->xselectionrequest.target,
+ xevent->xselectionrequest.time,
+ xevent->xselectionrequest.property);
+ return;
+ }
+ }
+ break;
+
+ case PropertyNotify:
+ // property delete may be part of a selection conversion
+ if (xevent->xproperty.state == PropertyDelete) {
+ processClipboardRequest(xevent->xproperty.window,
+ xevent->xproperty.time,
+ xevent->xproperty.atom);
+ }
+ break;
+
+ case DestroyNotify:
+ // looks like one of the windows that requested a clipboard
+ // transfer has gone bye-bye.
+ destroyClipboardRequest(xevent->xdestroywindow.window);
+ break;
+
+ case KeyPress:
+ if (m_isPrimary) {
+ onKeyPress(xevent->xkey);
+ }
+ return;
+
+ case KeyRelease:
+ if (m_isPrimary) {
+ onKeyRelease(xevent->xkey, isRepeat);
+ }
+ return;
+
+ case ButtonPress:
+ if (m_isPrimary) {
+ onMousePress(xevent->xbutton);
+ }
+ return;
+
+ case ButtonRelease:
+ if (m_isPrimary) {
+ onMouseRelease(xevent->xbutton);
+ }
+ return;
+
+ case MotionNotify:
+ if (m_isPrimary) {
+ onMouseMove(xevent->xmotion);
+ }
+ return;
+
+ default:
+#if HAVE_XKB_EXTENSION
+ if (m_xkb && xevent->type == m_xkbEventBase) {
+ XkbEvent* xkbEvent = reinterpret_cast<XkbEvent*>(xevent);
+ switch (xkbEvent->any.xkb_type) {
+ case XkbMapNotify:
+ refreshKeyboard(xevent);
+ return;
+
+ case XkbStateNotify:
+ LOG((CLOG_INFO "group change: %d", xkbEvent->state.group));
+ m_keyState->setActiveGroup((SInt32)xkbEvent->state.group);
+ return;
+ }
+ }
+#endif
+
+#if HAVE_X11_EXTENSIONS_XRANDR_H
+ if (m_xrandr) {
+ if (xevent->type == m_xrandrEventBase + RRScreenChangeNotify ||
+ (xevent->type == m_xrandrEventBase + RRNotify &&
+ reinterpret_cast<XRRNotifyEvent *>(xevent)->subtype == RRNotify_CrtcChange)) {
+ LOG((CLOG_INFO "XRRScreenChangeNotifyEvent or RRNotify_CrtcChange received"));
+
+ // we're required to call back into XLib so XLib can update its internal state
+ XRRUpdateConfiguration(xevent);
+
+ // requery/recalculate the screen shape
+ saveShape();
+
+ // we need to resize m_window, otherwise we'll get a weird problem where moving
+ // off the server onto the client causes the pointer to warp to the
+ // center of the server (so you can't move the pointer off the server)
+ if (m_isPrimary) {
+ XMoveWindow(m_display, m_window, m_x, m_y);
+ XResizeWindow(m_display, m_window, m_w, m_h);
+ }
+
+ sendEvent(m_events->forIScreen().shapeChanged());
+ }
+ }
+#endif
+
+ break;
+ }
+}
+
+void
+XWindowsScreen::onKeyPress(XKeyEvent& xkey)
+{
+ LOG((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xkey.keycode, xkey.state));
+ const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state);
+ KeyID key = mapKeyFromX(&xkey);
+ if (key != kKeyNone) {
+ // check for ctrl+alt+del emulation
+ if ((key == kKeyPause || key == kKeyBreak) &&
+ (mask & (KeyModifierControl | KeyModifierAlt)) ==
+ (KeyModifierControl | KeyModifierAlt)) {
+ // pretend it's ctrl+alt+del
+ LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
+ key = kKeyDelete;
+ }
+
+ // get which button. see call to XFilterEvent() in onEvent()
+ // for more info.
+ bool isFake = false;
+ KeyButton keycode = static_cast<KeyButton>(xkey.keycode);
+ if (keycode == 0) {
+ isFake = true;
+ keycode = static_cast<KeyButton>(m_lastKeycode);
+ if (keycode == 0) {
+ // no keycode
+ LOG((CLOG_DEBUG1 "event: KeyPress no keycode"));
+ return;
+ }
+ }
+
+ // handle key
+ m_keyState->sendKeyEvent(getEventTarget(),
+ true, false, key, mask, 1, keycode);
+
+ // do fake release if this is a fake press
+ if (isFake) {
+ m_keyState->sendKeyEvent(getEventTarget(),
+ false, false, key, mask, 1, keycode);
+ }
+ }
+ else {
+ LOG((CLOG_DEBUG1 "can't map keycode to key id"));
+ }
+}
+
+void
+XWindowsScreen::onKeyRelease(XKeyEvent& xkey, bool isRepeat)
+{
+ const KeyModifierMask mask = m_keyState->mapModifiersFromX(xkey.state);
+ KeyID key = mapKeyFromX(&xkey);
+ if (key != kKeyNone) {
+ // check for ctrl+alt+del emulation
+ if ((key == kKeyPause || key == kKeyBreak) &&
+ (mask & (KeyModifierControl | KeyModifierAlt)) ==
+ (KeyModifierControl | KeyModifierAlt)) {
+ // pretend it's ctrl+alt+del and ignore autorepeat
+ LOG((CLOG_DEBUG "emulate ctrl+alt+del"));
+ key = kKeyDelete;
+ isRepeat = false;
+ }
+
+ KeyButton keycode = static_cast<KeyButton>(xkey.keycode);
+ if (!isRepeat) {
+ // no press event follows so it's a plain release
+ LOG((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", keycode, xkey.state));
+ m_keyState->sendKeyEvent(getEventTarget(),
+ false, false, key, mask, 1, keycode);
+ }
+ else {
+ // found a press event following so it's a repeat.
+ // we could attempt to count the already queued
+ // repeats but we'll just send a repeat of 1.
+ // note that we discard the press event.
+ LOG((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", keycode, xkey.state));
+ m_keyState->sendKeyEvent(getEventTarget(),
+ false, true, key, mask, 1, keycode);
+ }
+ }
+}
+
+bool
+XWindowsScreen::onHotKey(XKeyEvent& xkey, bool isRepeat)
+{
+ // find the hot key id
+ HotKeyToIDMap::const_iterator i =
+ m_hotKeyToIDMap.find(HotKeyItem(xkey.keycode, xkey.state));
+ if (i == m_hotKeyToIDMap.end()) {
+ return false;
+ }
+
+ // find what kind of event
+ Event::Type type;
+ if (xkey.type == KeyPress) {
+ type = m_events->forIPrimaryScreen().hotKeyDown();
+ }
+ else if (xkey.type == KeyRelease) {
+ type = m_events->forIPrimaryScreen().hotKeyUp();
+ }
+ else {
+ return false;
+ }
+
+ // generate event (ignore key repeats)
+ if (!isRepeat) {
+ m_events->addEvent(Event(type, getEventTarget(),
+ HotKeyInfo::alloc(i->second)));
+ }
+ return true;
+}
+
+void
+XWindowsScreen::onMousePress(const XButtonEvent& xbutton)
+{
+ LOG((CLOG_DEBUG1 "event: ButtonPress button=%d", xbutton.button));
+ ButtonID button = mapButtonFromX(&xbutton);
+ KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state);
+ if (button != kButtonNone) {
+ sendEvent(m_events->forIPrimaryScreen().buttonDown(), ButtonInfo::alloc(button, mask));
+ }
+}
+
+void
+XWindowsScreen::onMouseRelease(const XButtonEvent& xbutton)
+{
+ LOG((CLOG_DEBUG1 "event: ButtonRelease button=%d", xbutton.button));
+ ButtonID button = mapButtonFromX(&xbutton);
+ KeyModifierMask mask = m_keyState->mapModifiersFromX(xbutton.state);
+ if (button != kButtonNone) {
+ sendEvent(m_events->forIPrimaryScreen().buttonUp(), ButtonInfo::alloc(button, mask));
+ }
+ else if (xbutton.button == 4) {
+ // wheel forward (away from user)
+ sendEvent(m_events->forIPrimaryScreen().wheel(), WheelInfo::alloc(0, 120));
+ }
+ else if (xbutton.button == 5) {
+ // wheel backward (toward user)
+ sendEvent(m_events->forIPrimaryScreen().wheel(), WheelInfo::alloc(0, -120));
+ }
+ // XXX -- support x-axis scrolling
+}
+
+void
+XWindowsScreen::onMouseMove(const XMotionEvent& xmotion)
+{
+ LOG((CLOG_DEBUG2 "event: MotionNotify %d,%d", xmotion.x_root, xmotion.y_root));
+
+ // compute motion delta (relative to the last known
+ // mouse position)
+ SInt32 x = xmotion.x_root - m_xCursor;
+ SInt32 y = xmotion.y_root - m_yCursor;
+
+ // save position to compute delta of next motion
+ m_xCursor = xmotion.x_root;
+ m_yCursor = xmotion.y_root;
+
+ if (xmotion.send_event) {
+ // we warped the mouse. discard events until we
+ // find the matching sent event. see
+ // warpCursorNoFlush() for where the events are
+ // sent. we discard the matching sent event and
+ // can be sure we've skipped the warp event.
+ XEvent xevent;
+ char cntr = 0;
+ do {
+ XMaskEvent(m_display, PointerMotionMask, &xevent);
+ if (cntr++ > 10) {
+ LOG((CLOG_WARN "too many discarded events! %d", cntr));
+ break;
+ }
+ } while (!xevent.xany.send_event);
+ cntr = 0;
+ }
+ else if (m_isOnScreen) {
+ // motion on primary screen
+ sendEvent(m_events->forIPrimaryScreen().motionOnPrimary(),
+ MotionInfo::alloc(m_xCursor, m_yCursor));
+ }
+ else {
+ // motion on secondary screen. warp mouse back to
+ // center.
+ //
+ // my lombard (powerbook g3) running linux and
+ // using the adbmouse driver has two problems:
+ // first, the driver only sends motions of +/-2
+ // pixels and, second, it seems to discard some
+ // physical input after a warp. the former isn't a
+ // big deal (we're just limited to every other
+ // pixel) but the latter is a PITA. to work around
+ // it we only warp when the mouse has moved more
+ // than s_size pixels from the center.
+ static const SInt32 s_size = 32;
+ if (xmotion.x_root - m_xCenter < -s_size ||
+ xmotion.x_root - m_xCenter > s_size ||
+ xmotion.y_root - m_yCenter < -s_size ||
+ xmotion.y_root - m_yCenter > s_size) {
+ warpCursorNoFlush(m_xCenter, m_yCenter);
+ }
+
+ // send event if mouse moved. do this after warping
+ // back to center in case the motion takes us onto
+ // the primary screen. if we sent the event first
+ // in that case then the warp would happen after
+ // warping to the primary screen's enter position,
+ // effectively overriding it.
+ if (x != 0 || y != 0) {
+ sendEvent(m_events->forIPrimaryScreen().motionOnSecondary(), MotionInfo::alloc(x, y));
+ }
+ }
+}
+
+Cursor
+XWindowsScreen::createBlankCursor() const
+{
+ // this seems just a bit more complicated than really necessary
+
+ // get the closet cursor size to 1x1
+ unsigned int w = 0, h = 0;
+ XQueryBestCursor(m_display, m_root, 1, 1, &w, &h);
+ w = std::max(1u, w);
+ h = std::max(1u, h);
+
+ // make bitmap data for cursor of closet size. since the cursor
+ // is blank we can use the same bitmap for shape and mask: all
+ // zeros.
+ const int size = ((w + 7) >> 3) * h;
+ char* data = new char[size];
+ memset(data, 0, size);
+
+ // make bitmap
+ Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h);
+
+ // need an arbitrary color for the cursor
+ XColor color;
+ color.pixel = 0;
+ color.red = color.green = color.blue = 0;
+ color.flags = DoRed | DoGreen | DoBlue;
+
+ // make cursor from bitmap
+ Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap,
+ &color, &color, 0, 0);
+
+ // don't need bitmap or the data anymore
+ delete[] data;
+ XFreePixmap(m_display, bitmap);
+
+ return cursor;
+}
+
+ClipboardID
+XWindowsScreen::getClipboardID(Atom selection) const
+{
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ if (m_clipboard[id] != NULL &&
+ m_clipboard[id]->getSelection() == selection) {
+ return id;
+ }
+ }
+ return kClipboardEnd;
+}
+
+void
+XWindowsScreen::processClipboardRequest(Window requestor,
+ Time time, Atom property)
+{
+ // check every clipboard until one returns success
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ if (m_clipboard[id] != NULL &&
+ m_clipboard[id]->processRequest(requestor, time, property)) {
+ break;
+ }
+ }
+}
+
+void
+XWindowsScreen::destroyClipboardRequest(Window requestor)
+{
+ // check every clipboard until one returns success
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ if (m_clipboard[id] != NULL &&
+ m_clipboard[id]->destroyRequest(requestor)) {
+ break;
+ }
+ }
+}
+
+void
+XWindowsScreen::onError()
+{
+ // prevent further access to the X display
+ m_events->adoptBuffer(NULL);
+ m_screensaver->destroy();
+ m_screensaver = NULL;
+ m_display = NULL;
+
+ // notify of failure
+ sendEvent(m_events->forIScreen().error(), NULL);
+
+ // FIXME -- should ensure that we ignore operations that involve
+ // m_display from now on. however, Xlib will simply exit the
+ // application in response to the X I/O error so there's no
+ // point in trying to really handle the error. if we did want
+ // to handle the error, it'd probably be easiest to delegate to
+ // one of two objects. one object would take the implementation
+ // from this class. the other object would be stub methods that
+ // don't use X11. on error, we'd switch to the latter.
+}
+
+int
+XWindowsScreen::ioErrorHandler(Display*)
+{
+ // the display has disconnected, probably because X is shutting
+ // down. X forces us to exit at this point which is annoying.
+ // we'll pretend as if we won't exit so we try to make sure we
+ // don't access the display anymore.
+ LOG((CLOG_CRIT "X display has unexpectedly disconnected"));
+ s_screen->onError();
+ return 0;
+}
+
+void
+XWindowsScreen::selectEvents(Window w) const
+{
+ // ignore errors while we adjust event masks. windows could be
+ // destroyed at any time after the XQueryTree() in doSelectEvents()
+ // so we must ignore BadWindow errors.
+ XWindowsUtil::ErrorLock lock(m_display);
+
+ // adjust event masks
+ doSelectEvents(w);
+}
+
+void
+XWindowsScreen::doSelectEvents(Window w) const
+{
+ // we want to track the mouse everywhere on the display. to achieve
+ // that we select PointerMotionMask on every window. we also select
+ // SubstructureNotifyMask in order to get CreateNotify events so we
+ // select events on new windows too.
+
+ // we don't want to adjust our grab window
+ if (w == m_window) {
+ return;
+ }
+
+ // X11 has a design flaw. If *no* client selected PointerMotionMask for
+ // a window, motion events will be delivered to that window's parent.
+ // If *any* client, not necessarily the owner, selects PointerMotionMask
+ // on such a window, X will stop propagating motion events to its
+ // parent. This breaks applications that rely on event propagation
+ // behavior.
+ //
+ // Avoid selecting PointerMotionMask unless some other client selected
+ // it already.
+ long mask = SubstructureNotifyMask;
+ XWindowAttributes attr;
+ XGetWindowAttributes(m_display, w, &attr);
+ if ((attr.all_event_masks & PointerMotionMask) == PointerMotionMask) {
+ mask |= PointerMotionMask;
+ }
+
+ // select events of interest. do this before querying the tree so
+ // we'll get notifications of children created after the XQueryTree()
+ // so we won't miss them.
+ XSelectInput(m_display, w, mask);
+
+ // recurse on child windows
+ Window rw, pw, *cw;
+ unsigned int nc;
+ if (XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) {
+ for (unsigned int i = 0; i < nc; ++i) {
+ doSelectEvents(cw[i]);
+ }
+ XFree(cw);
+ }
+}
+
+KeyID
+XWindowsScreen::mapKeyFromX(XKeyEvent* event) const
+{
+ // convert to a keysym
+ KeySym keysym;
+ if (event->type == KeyPress && m_ic != NULL) {
+ // do multibyte lookup. can only call XmbLookupString with a
+ // key press event and a valid XIC so we checked those above.
+ char scratch[32];
+ int n = sizeof(scratch) / sizeof(scratch[0]);
+ char* buffer = scratch;
+ int status;
+ n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status);
+ if (status == XBufferOverflow) {
+ // not enough space. grow buffer and try again.
+ buffer = new char[n];
+ n = XmbLookupString(m_ic, event, buffer, n, &keysym, &status);
+ delete[] buffer;
+ }
+
+ // see what we got. since we don't care about the string
+ // we'll just look for a keysym.
+ switch (status) {
+ default:
+ case XLookupNone:
+ case XLookupChars:
+ keysym = 0;
+ break;
+
+ case XLookupKeySym:
+ case XLookupBoth:
+ break;
+ }
+ }
+ else {
+ // plain old lookup
+ char dummy[1];
+ XLookupString(event, dummy, 0, &keysym, NULL);
+ }
+
+ LOG((CLOG_DEBUG2 "mapped code=%d to keysym=0x%04x", event->keycode, keysym));
+
+ // convert key
+ KeyID result = XWindowsUtil::mapKeySymToKeyID(keysym);
+ LOG((CLOG_DEBUG2 "mapped keysym=0x%04x to keyID=%d", keysym, result));
+ return result;
+}
+
+ButtonID
+XWindowsScreen::mapButtonFromX(const XButtonEvent* event) const
+{
+ unsigned int button = event->button;
+
+ // first three buttons map to 1, 2, 3 (kButtonLeft, Middle, Right)
+ if (button >= 1 && button <= 3) {
+ return static_cast<ButtonID>(button);
+ }
+
+ // buttons 4 and 5 are ignored here. they're used for the wheel.
+ // buttons 6, 7, etc and up map to 4, 5, etc.
+ else if (button >= 6) {
+ return static_cast<ButtonID>(button - 2);
+ }
+
+ // unknown button
+ else {
+ return kButtonNone;
+ }
+}
+
+unsigned int
+XWindowsScreen::mapButtonToX(ButtonID id) const
+{
+ // map button -1 to button 4 (+wheel)
+ if (id == static_cast<ButtonID>(-1)) {
+ id = 4;
+ }
+
+ // map button -2 to button 5 (-wheel)
+ else if (id == static_cast<ButtonID>(-2)) {
+ id = 5;
+ }
+
+ // map buttons 4, 5, etc. to 6, 7, etc. to make room for buttons
+ // 4 and 5 used to simulate the mouse wheel.
+ else if (id >= 4) {
+ id += 2;
+ }
+
+ // check button is in legal range
+ if (id < 1 || id > m_buttons.size()) {
+ // out of range
+ return 0;
+ }
+
+ // map button
+ return static_cast<unsigned int>(id);
+}
+
+void
+XWindowsScreen::warpCursorNoFlush(SInt32 x, SInt32 y)
+{
+ assert(m_window != None);
+
+ // send an event that we can recognize before the mouse warp
+ XEvent eventBefore;
+ eventBefore.type = MotionNotify;
+ eventBefore.xmotion.display = m_display;
+ eventBefore.xmotion.window = m_window;
+ eventBefore.xmotion.root = m_root;
+ eventBefore.xmotion.subwindow = m_window;
+ eventBefore.xmotion.time = CurrentTime;
+ eventBefore.xmotion.x = x;
+ eventBefore.xmotion.y = y;
+ eventBefore.xmotion.x_root = x;
+ eventBefore.xmotion.y_root = y;
+ eventBefore.xmotion.state = 0;
+ eventBefore.xmotion.is_hint = NotifyNormal;
+ eventBefore.xmotion.same_screen = True;
+ XEvent eventAfter = eventBefore;
+ XSendEvent(m_display, m_window, False, 0, &eventBefore);
+
+ // warp mouse
+ XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y);
+
+ // send an event that we can recognize after the mouse warp
+ XSendEvent(m_display, m_window, False, 0, &eventAfter);
+ XSync(m_display, False);
+
+ LOG((CLOG_DEBUG2 "warped to %d,%d", x, y));
+}
+
+void
+XWindowsScreen::updateButtons()
+{
+ // query the button mapping
+ UInt32 numButtons = XGetPointerMapping(m_display, NULL, 0);
+ unsigned char* tmpButtons = new unsigned char[numButtons];
+ XGetPointerMapping(m_display, tmpButtons, numButtons);
+
+ // find the largest logical button id
+ unsigned char maxButton = 0;
+ for (UInt32 i = 0; i < numButtons; ++i) {
+ if (tmpButtons[i] > maxButton) {
+ maxButton = tmpButtons[i];
+ }
+ }
+
+ // allocate button array
+ m_buttons.resize(maxButton);
+
+ // fill in button array values. m_buttons[i] is the physical
+ // button number for logical button i+1.
+ for (UInt32 i = 0; i < numButtons; ++i) {
+ m_buttons[i] = 0;
+ }
+ for (UInt32 i = 0; i < numButtons; ++i) {
+ m_buttons[tmpButtons[i] - 1] = i + 1;
+ }
+
+ // clean up
+ delete[] tmpButtons;
+}
+
+bool
+XWindowsScreen::grabMouseAndKeyboard()
+{
+ unsigned int event_mask = ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask;
+
+ // grab the mouse and keyboard. keep trying until we get them.
+ // if we can't grab one after grabbing the other then ungrab
+ // and wait before retrying. give up after s_timeout seconds.
+ static const double s_timeout = 1.0;
+ int result;
+ Stopwatch timer;
+ do {
+ // keyboard first
+ do {
+ result = XGrabKeyboard(m_display, m_window, True,
+ GrabModeAsync, GrabModeAsync, CurrentTime);
+ assert(result != GrabNotViewable);
+ if (result != GrabSuccess) {
+ LOG((CLOG_DEBUG2 "waiting to grab keyboard"));
+ ARCH->sleep(0.05);
+ if (timer.getTime() >= s_timeout) {
+ LOG((CLOG_DEBUG2 "grab keyboard timed out"));
+ return false;
+ }
+ }
+ } while (result != GrabSuccess);
+ LOG((CLOG_DEBUG2 "grabbed keyboard"));
+
+ // now the mouse --- use event_mask to get EnterNotify, LeaveNotify events
+ result = XGrabPointer(m_display, m_window, False, event_mask,
+ GrabModeAsync, GrabModeAsync,
+ m_window, None, CurrentTime);
+ assert(result != GrabNotViewable);
+ if (result != GrabSuccess) {
+ // back off to avoid grab deadlock
+ XUngrabKeyboard(m_display, CurrentTime);
+ LOG((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer"));
+ ARCH->sleep(0.05);
+ if (timer.getTime() >= s_timeout) {
+ LOG((CLOG_DEBUG2 "grab pointer timed out"));
+ return false;
+ }
+ }
+ } while (result != GrabSuccess);
+
+ LOG((CLOG_DEBUG1 "grabbed pointer and keyboard"));
+ return true;
+}
+
+void
+XWindowsScreen::refreshKeyboard(XEvent* event)
+{
+ if (XPending(m_display) > 0) {
+ XEvent tmpEvent;
+ XPeekEvent(m_display, &tmpEvent);
+ if (tmpEvent.type == MappingNotify) {
+ // discard this event since another follows.
+ // we tend to get a bunch of these in a row.
+ return;
+ }
+ }
+
+ // keyboard mapping changed
+#if HAVE_XKB_EXTENSION
+ if (m_xkb && event->type == m_xkbEventBase) {
+ XkbRefreshKeyboardMapping((XkbMapNotifyEvent*)event);
+ }
+ else
+#else
+ {
+ XRefreshKeyboardMapping(&event->xmapping);
+ }
+#endif
+ m_keyState->updateKeyMap();
+ m_keyState->updateKeyState();
+}
+
+
+//
+// XWindowsScreen::HotKeyItem
+//
+
+XWindowsScreen::HotKeyItem::HotKeyItem(int keycode, unsigned int mask) :
+ m_keycode(keycode),
+ m_mask(mask)
+{
+ // do nothing
+}
+
+bool
+XWindowsScreen::HotKeyItem::operator<(const HotKeyItem& x) const
+{
+ return (m_keycode < x.m_keycode ||
+ (m_keycode == x.m_keycode && m_mask < x.m_mask));
+}
+
+bool
+XWindowsScreen::detectXI2()
+{
+ int event, error;
+ return XQueryExtension(m_display,
+ "XInputExtension", &xi_opcode, &event, &error);
+}
+
+#ifdef HAVE_XI2
+void
+XWindowsScreen::selectXIRawMotion()
+{
+ XIEventMask mask;
+
+ mask.deviceid = XIAllDevices;
+ mask.mask_len = XIMaskLen(XI_RawMotion);
+ mask.mask = (unsigned char*)calloc(mask.mask_len, sizeof(char));
+ mask.deviceid = XIAllMasterDevices;
+ memset(mask.mask, 0, 2);
+ XISetMask(mask.mask, XI_RawKeyRelease);
+ XISetMask(mask.mask, XI_RawMotion);
+ XISelectEvents(m_display, DefaultRootWindow(m_display), &mask, 1);
+ free(mask.mask);
+}
+#endif
diff --git a/src/lib/platform/XWindowsScreen.h b/src/lib/platform/XWindowsScreen.h
new file mode 100644
index 0000000..35f9368
--- /dev/null
+++ b/src/lib/platform/XWindowsScreen.h
@@ -0,0 +1,252 @@
+/*
+ * barrier -- 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 "barrier/PlatformScreen.h"
+#include "barrier/KeyMap.h"
+#include "common/stdset.h"
+#include "common/stdvector.h"
+
+#if X_DISPLAY_MISSING
+# error X11 is required to build barrier
+#else
+# include <X11/Xlib.h>
+#endif
+
+class XWindowsClipboard;
+class XWindowsKeyState;
+class XWindowsScreenSaver;
+
+//! Implementation of IPlatformScreen for X11
+class XWindowsScreen : public PlatformScreen {
+public:
+ XWindowsScreen(const char* displayName, bool isPrimary,
+ bool disableXInitThreads, int mouseScrollDelta,
+ IEventQueue* events);
+ virtual ~XWindowsScreen();
+
+ //! @name manipulators
+ //@{
+
+ //@}
+
+ // IScreen overrides
+ virtual void* getEventTarget() const;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const;
+
+ // IPrimaryScreen overrides
+ virtual void reconfigure(UInt32 activeSides);
+ virtual void warpCursor(SInt32 x, SInt32 y);
+ virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask);
+ virtual void unregisterHotKey(UInt32 id);
+ virtual void fakeInputBegin();
+ virtual void fakeInputEnd();
+ virtual SInt32 getJumpZoneSize() const;
+ virtual bool isAnyMouseButtonDown(UInt32& buttonID) const;
+ virtual void getCursorCenter(SInt32& x, SInt32& y) const;
+
+ // ISecondaryScreen overrides
+ virtual void fakeMouseButton(ButtonID id, bool press);
+ virtual void fakeMouseMove(SInt32 x, SInt32 y);
+ virtual void fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const;
+ virtual void fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
+
+ // IPlatformScreen overrides
+ virtual void enable();
+ virtual void disable();
+ virtual void enter();
+ virtual bool leave();
+ virtual bool setClipboard(ClipboardID, const IClipboard*);
+ virtual void checkClipboards();
+ virtual void openScreensaver(bool notify);
+ virtual void closeScreensaver();
+ virtual void screensaver(bool activate);
+ virtual void resetOptions();
+ virtual void setOptions(const OptionsList& options);
+ virtual void setSequenceNumber(UInt32);
+ virtual bool isPrimary() const;
+
+protected:
+ // IPlatformScreen overrides
+ virtual void handleSystemEvent(const Event&, void*);
+ virtual void updateButtons();
+ virtual IKeyState* getKeyState() const;
+
+private:
+ // event sending
+ void sendEvent(Event::Type, void* = NULL);
+ void sendClipboardEvent(Event::Type, ClipboardID);
+
+ // create the transparent cursor
+ Cursor createBlankCursor() const;
+
+ // determine the clipboard from the X selection. returns
+ // kClipboardEnd if no such clipboard.
+ ClipboardID getClipboardID(Atom selection) const;
+
+ // continue processing a selection request
+ void processClipboardRequest(Window window,
+ Time time, Atom property);
+
+ // terminate a selection request
+ void destroyClipboardRequest(Window window);
+
+ // X I/O error handler
+ void onError();
+ static int ioErrorHandler(Display*);
+
+private:
+ class KeyEventFilter {
+ public:
+ int m_event;
+ Window m_window;
+ Time m_time;
+ KeyCode m_keycode;
+ };
+
+ Display* openDisplay(const char* displayName);
+ void saveShape();
+ Window openWindow() const;
+ void openIM();
+
+ bool grabMouseAndKeyboard();
+ void onKeyPress(XKeyEvent&);
+ void onKeyRelease(XKeyEvent&, bool isRepeat);
+ bool onHotKey(XKeyEvent&, bool isRepeat);
+ void onMousePress(const XButtonEvent&);
+ void onMouseRelease(const XButtonEvent&);
+ void onMouseMove(const XMotionEvent&);
+
+ bool detectXI2();
+#ifdef HAVE_XI2
+ void selectXIRawMotion();
+#endif
+ void selectEvents(Window) const;
+ void doSelectEvents(Window) const;
+
+ KeyID mapKeyFromX(XKeyEvent*) const;
+ ButtonID mapButtonFromX(const XButtonEvent*) const;
+ unsigned int mapButtonToX(ButtonID id) const;
+
+ void warpCursorNoFlush(SInt32 x, SInt32 y);
+
+ void refreshKeyboard(XEvent*);
+
+ static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg);
+
+private:
+ struct HotKeyItem {
+ public:
+ HotKeyItem(int, unsigned int);
+
+ bool operator<(const HotKeyItem&) const;
+
+ private:
+ int m_keycode;
+ unsigned int m_mask;
+ };
+ typedef std::set<bool> FilteredKeycodes;
+ typedef std::vector<std::pair<int, unsigned int> > HotKeyList;
+ typedef std::map<UInt32, HotKeyList> HotKeyMap;
+ typedef std::vector<UInt32> HotKeyIDList;
+ typedef std::map<HotKeyItem, UInt32> HotKeyToIDMap;
+
+ // true if screen is being used as a primary screen, false otherwise
+ bool m_isPrimary;
+ int m_mouseScrollDelta;
+
+ Display* m_display;
+ Window m_root;
+ Window m_window;
+
+ // true if mouse has entered the screen
+ bool m_isOnScreen;
+
+ // screen shape stuff
+ SInt32 m_x, m_y;
+ SInt32 m_w, m_h;
+ SInt32 m_xCenter, m_yCenter;
+
+ // last mouse position
+ SInt32 m_xCursor, m_yCursor;
+
+ // keyboard stuff
+ XWindowsKeyState* m_keyState;
+
+ // hot key stuff
+ HotKeyMap m_hotKeys;
+ HotKeyIDList m_oldHotKeyIDs;
+ HotKeyToIDMap m_hotKeyToIDMap;
+
+ // input focus stuff
+ Window m_lastFocus;
+ int m_lastFocusRevert;
+
+ // input method stuff
+ XIM m_im;
+ XIC m_ic;
+ KeyCode m_lastKeycode;
+ FilteredKeycodes m_filtered;
+
+ // clipboards
+ XWindowsClipboard* m_clipboard[kClipboardEnd];
+ UInt32 m_sequenceNumber;
+
+ // screen saver stuff
+ XWindowsScreenSaver* m_screensaver;
+ bool m_screensaverNotify;
+
+ // logical to physical button mapping. m_buttons[i] gives the
+ // physical button for logical button i+1.
+ std::vector<unsigned char> m_buttons;
+
+ // true if global auto-repeat was enabled before we turned it off
+ bool m_autoRepeat;
+
+ // stuff to workaround xtest being xinerama unaware. attempting
+ // to fake a mouse motion under xinerama may behave strangely,
+ // especially if screen 0 is not at 0,0 or if faking a motion on
+ // a screen other than screen 0.
+ bool m_xtestIsXineramaUnaware;
+ bool m_xinerama;
+
+ // stuff to work around lost focus issues on certain systems
+ // (ie: a MythTV front-end).
+ bool m_preserveFocus;
+
+ // XKB extension stuff
+ bool m_xkb;
+ int m_xkbEventBase;
+
+ bool m_xi2detected;
+
+ // XRandR extension stuff
+ bool m_xrandr;
+ int m_xrandrEventBase;
+
+ IEventQueue* m_events;
+ barrier::KeyMap m_keyMap;
+
+ // pointer to (singleton) screen. this is only needed by
+ // ioErrorHandler().
+ static XWindowsScreen* s_screen;
+};
diff --git a/src/lib/platform/XWindowsScreenSaver.cpp b/src/lib/platform/XWindowsScreenSaver.cpp
new file mode 100644
index 0000000..bc457f9
--- /dev/null
+++ b/src/lib/platform/XWindowsScreenSaver.cpp
@@ -0,0 +1,605 @@
+/*
+ * barrier -- 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 "platform/XWindowsScreenSaver.h"
+
+#include "platform/XWindowsUtil.h"
+#include "barrier/IPlatformScreen.h"
+#include "base/Log.h"
+#include "base/Event.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+
+#include <X11/Xatom.h>
+#if HAVE_X11_EXTENSIONS_XTEST_H
+# include <X11/extensions/XTest.h>
+#else
+# error The XTest extension is required to build barrier
+#endif
+#if HAVE_X11_EXTENSIONS_DPMS_H
+extern "C" {
+# include <X11/Xmd.h>
+# include <X11/extensions/dpms.h>
+# if !HAVE_DPMS_PROTOTYPES
+# undef DPMSModeOn
+# undef DPMSModeStandby
+# undef DPMSModeSuspend
+# undef DPMSModeOff
+# define DPMSModeOn 0
+# define DPMSModeStandby 1
+# define DPMSModeSuspend 2
+# define DPMSModeOff 3
+extern Bool DPMSQueryExtension(Display *, int *, int *);
+extern Bool DPMSCapable(Display *);
+extern Status DPMSEnable(Display *);
+extern Status DPMSDisable(Display *);
+extern Status DPMSForceLevel(Display *, CARD16);
+extern Status DPMSInfo(Display *, CARD16 *, BOOL *);
+# endif
+}
+#endif
+
+//
+// XWindowsScreenSaver
+//
+
+XWindowsScreenSaver::XWindowsScreenSaver(
+ Display* display, Window window, void* eventTarget, IEventQueue* events) :
+ m_display(display),
+ m_xscreensaverSink(window),
+ m_eventTarget(eventTarget),
+ m_xscreensaver(None),
+ m_xscreensaverActive(false),
+ m_dpms(false),
+ m_disabled(false),
+ m_suppressDisable(false),
+ m_disableTimer(NULL),
+ m_disablePos(0),
+ m_events(events)
+{
+ // get atoms
+ m_atomScreenSaver = XInternAtom(m_display,
+ "SCREENSAVER", False);
+ m_atomScreenSaverVersion = XInternAtom(m_display,
+ "_SCREENSAVER_VERSION", False);
+ m_atomScreenSaverActivate = XInternAtom(m_display,
+ "ACTIVATE", False);
+ m_atomScreenSaverDeactivate = XInternAtom(m_display,
+ "DEACTIVATE", False);
+
+ // check for DPMS extension. this is an alternative screen saver
+ // that powers down the display.
+#if HAVE_X11_EXTENSIONS_DPMS_H
+ int eventBase, errorBase;
+ if (DPMSQueryExtension(m_display, &eventBase, &errorBase)) {
+ if (DPMSCapable(m_display)) {
+ // we have DPMS
+ m_dpms = true;
+ }
+ }
+#endif
+
+ // watch top-level windows for changes
+ bool error = false;
+ {
+ XWindowsUtil::ErrorLock lock(m_display, &error);
+ Window root = DefaultRootWindow(m_display);
+ XWindowAttributes attr;
+ XGetWindowAttributes(m_display, root, &attr);
+ m_rootEventMask = attr.your_event_mask;
+ XSelectInput(m_display, root, m_rootEventMask | SubstructureNotifyMask);
+ }
+ if (error) {
+ LOG((CLOG_DEBUG "didn't set root event mask"));
+ m_rootEventMask = 0;
+ }
+
+ // get the built-in settings
+ XGetScreenSaver(m_display, &m_timeout, &m_interval,
+ &m_preferBlanking, &m_allowExposures);
+
+ // get the DPMS settings
+ m_dpmsEnabled = isDPMSEnabled();
+
+ // get the xscreensaver window, if any
+ if (!findXScreenSaver()) {
+ setXScreenSaver(None);
+ }
+
+ // install disable timer event handler
+ m_events->adoptHandler(Event::kTimer, this,
+ new TMethodEventJob<XWindowsScreenSaver>(this,
+ &XWindowsScreenSaver::handleDisableTimer));
+}
+
+XWindowsScreenSaver::~XWindowsScreenSaver()
+{
+ // done with disable job
+ if (m_disableTimer != NULL) {
+ m_events->deleteTimer(m_disableTimer);
+ }
+ m_events->removeHandler(Event::kTimer, this);
+
+ if (m_display != NULL) {
+ enableDPMS(m_dpmsEnabled);
+ XSetScreenSaver(m_display, m_timeout, m_interval,
+ m_preferBlanking, m_allowExposures);
+ clearWatchForXScreenSaver();
+ XWindowsUtil::ErrorLock lock(m_display);
+ XSelectInput(m_display, DefaultRootWindow(m_display), m_rootEventMask);
+ }
+}
+
+void
+XWindowsScreenSaver::destroy()
+{
+ m_display = NULL;
+ delete this;
+}
+
+bool
+XWindowsScreenSaver::handleXEvent(const XEvent* xevent)
+{
+ switch (xevent->type) {
+ case CreateNotify:
+ if (m_xscreensaver == None) {
+ if (isXScreenSaver(xevent->xcreatewindow.window)) {
+ // found the xscreensaver
+ setXScreenSaver(xevent->xcreatewindow.window);
+ }
+ else {
+ // another window to watch. to detect the xscreensaver
+ // window we look for a property but that property may
+ // not yet exist by the time we get this event so we
+ // have to watch the window for property changes.
+ // this would be so much easier if xscreensaver did the
+ // smart thing and stored its window in a property on
+ // the root window.
+ addWatchXScreenSaver(xevent->xcreatewindow.window);
+ }
+ }
+ break;
+
+ case DestroyNotify:
+ if (xevent->xdestroywindow.window == m_xscreensaver) {
+ // xscreensaver is gone
+ LOG((CLOG_DEBUG "xscreensaver died"));
+ setXScreenSaver(None);
+ return true;
+ }
+ break;
+
+ case PropertyNotify:
+ if (xevent->xproperty.state == PropertyNewValue) {
+ if (isXScreenSaver(xevent->xproperty.window)) {
+ // found the xscreensaver
+ setXScreenSaver(xevent->xcreatewindow.window);
+ }
+ }
+ break;
+
+ case MapNotify:
+ if (xevent->xmap.window == m_xscreensaver) {
+ // xscreensaver has activated
+ setXScreenSaverActive(true);
+ return true;
+ }
+ break;
+
+ case UnmapNotify:
+ if (xevent->xunmap.window == m_xscreensaver) {
+ // xscreensaver has deactivated
+ setXScreenSaverActive(false);
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+void
+XWindowsScreenSaver::enable()
+{
+ // for xscreensaver
+ m_disabled = false;
+ updateDisableTimer();
+
+ // for built-in X screen saver
+ XSetScreenSaver(m_display, m_timeout, m_interval,
+ m_preferBlanking, m_allowExposures);
+
+ // for DPMS
+ enableDPMS(m_dpmsEnabled);
+}
+
+void
+XWindowsScreenSaver::disable()
+{
+ // for xscreensaver
+ m_disabled = true;
+ updateDisableTimer();
+
+ // use built-in X screen saver
+ XGetScreenSaver(m_display, &m_timeout, &m_interval,
+ &m_preferBlanking, &m_allowExposures);
+ XSetScreenSaver(m_display, 0, m_interval,
+ m_preferBlanking, m_allowExposures);
+
+ // for DPMS
+ m_dpmsEnabled = isDPMSEnabled();
+ enableDPMS(false);
+
+ // FIXME -- now deactivate?
+}
+
+void
+XWindowsScreenSaver::activate()
+{
+ // remove disable job timer
+ m_suppressDisable = true;
+ updateDisableTimer();
+
+ // enable DPMS if it was enabled
+ enableDPMS(m_dpmsEnabled);
+
+ // try xscreensaver
+ findXScreenSaver();
+ if (m_xscreensaver != None) {
+ sendXScreenSaverCommand(m_atomScreenSaverActivate);
+ return;
+ }
+
+ // try built-in X screen saver
+ if (m_timeout != 0) {
+ XForceScreenSaver(m_display, ScreenSaverActive);
+ }
+
+ // try DPMS
+ activateDPMS(true);
+}
+
+void
+XWindowsScreenSaver::deactivate()
+{
+ // reinstall disable job timer
+ m_suppressDisable = false;
+ updateDisableTimer();
+
+ // try DPMS
+ activateDPMS(false);
+
+ // disable DPMS if screen saver is disabled
+ if (m_disabled) {
+ enableDPMS(false);
+ }
+
+ // try xscreensaver
+ findXScreenSaver();
+ if (m_xscreensaver != None) {
+ sendXScreenSaverCommand(m_atomScreenSaverDeactivate);
+ return;
+ }
+
+ // use built-in X screen saver
+ XForceScreenSaver(m_display, ScreenSaverReset);
+}
+
+bool
+XWindowsScreenSaver::isActive() const
+{
+ // check xscreensaver
+ if (m_xscreensaver != None) {
+ return m_xscreensaverActive;
+ }
+
+ // check DPMS
+ if (isDPMSActivated()) {
+ return true;
+ }
+
+ // can't check built-in X screen saver activity
+ return false;
+}
+
+bool
+XWindowsScreenSaver::findXScreenSaver()
+{
+ // do nothing if we've already got the xscreensaver window
+ if (m_xscreensaver == None) {
+ // find top-level window xscreensaver window
+ Window root = DefaultRootWindow(m_display);
+ Window rw, pw, *cw;
+ unsigned int nc;
+ if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) {
+ for (unsigned int i = 0; i < nc; ++i) {
+ if (isXScreenSaver(cw[i])) {
+ setXScreenSaver(cw[i]);
+ break;
+ }
+ }
+ XFree(cw);
+ }
+ }
+
+ return (m_xscreensaver != None);
+}
+
+void
+XWindowsScreenSaver::setXScreenSaver(Window window)
+{
+ LOG((CLOG_DEBUG "xscreensaver window: 0x%08x", window));
+
+ // save window
+ m_xscreensaver = window;
+
+ if (m_xscreensaver != None) {
+ // clear old watch list
+ clearWatchForXScreenSaver();
+
+ // see if xscreensaver is active
+ bool error = false;
+ XWindowAttributes attr;
+ {
+ XWindowsUtil::ErrorLock lock(m_display, &error);
+ XGetWindowAttributes(m_display, m_xscreensaver, &attr);
+ }
+ setXScreenSaverActive(!error && attr.map_state != IsUnmapped);
+
+ // save current DPMS state; xscreensaver may have changed it.
+ m_dpmsEnabled = isDPMSEnabled();
+ }
+ else {
+ // screen saver can't be active if it doesn't exist
+ setXScreenSaverActive(false);
+
+ // start watching for xscreensaver
+ watchForXScreenSaver();
+ }
+}
+
+bool
+XWindowsScreenSaver::isXScreenSaver(Window w) const
+{
+ // check for m_atomScreenSaverVersion string property
+ Atom type;
+ return (XWindowsUtil::getWindowProperty(m_display, w,
+ m_atomScreenSaverVersion,
+ NULL, &type, NULL, False) &&
+ type == XA_STRING);
+}
+
+void
+XWindowsScreenSaver::setXScreenSaverActive(bool activated)
+{
+ if (m_xscreensaverActive != activated) {
+ LOG((CLOG_DEBUG "xscreensaver %s on window 0x%08x", activated ? "activated" : "deactivated", m_xscreensaver));
+ m_xscreensaverActive = activated;
+
+ // if screen saver was activated forcefully (i.e. against
+ // our will) then just accept it. don't try to keep it
+ // from activating since that'll just pop up the password
+ // dialog if locking is enabled.
+ m_suppressDisable = activated;
+ updateDisableTimer();
+
+ if (activated) {
+ m_events->addEvent(Event(
+ m_events->forIPrimaryScreen().screensaverActivated(),
+ m_eventTarget));
+ }
+ else {
+ m_events->addEvent(Event(
+ m_events->forIPrimaryScreen().screensaverDeactivated(),
+ m_eventTarget));
+ }
+ }
+}
+
+void
+XWindowsScreenSaver::sendXScreenSaverCommand(Atom cmd, long arg1, long arg2)
+{
+ XEvent event;
+ event.xclient.type = ClientMessage;
+ event.xclient.display = m_display;
+ event.xclient.window = m_xscreensaverSink;
+ event.xclient.message_type = m_atomScreenSaver;
+ event.xclient.format = 32;
+ event.xclient.data.l[0] = static_cast<long>(cmd);
+ event.xclient.data.l[1] = arg1;
+ event.xclient.data.l[2] = arg2;
+ event.xclient.data.l[3] = 0;
+ event.xclient.data.l[4] = 0;
+
+ LOG((CLOG_DEBUG "send xscreensaver command: %d %d %d", (long)cmd, arg1, arg2));
+ bool error = false;
+ {
+ XWindowsUtil::ErrorLock lock(m_display, &error);
+ XSendEvent(m_display, m_xscreensaver, False, 0, &event);
+ }
+ if (error) {
+ findXScreenSaver();
+ }
+}
+
+void
+XWindowsScreenSaver::watchForXScreenSaver()
+{
+ // clear old watch list
+ clearWatchForXScreenSaver();
+
+ // add every child of the root to the list of windows to watch
+ Window root = DefaultRootWindow(m_display);
+ Window rw, pw, *cw;
+ unsigned int nc;
+ if (XQueryTree(m_display, root, &rw, &pw, &cw, &nc)) {
+ for (unsigned int i = 0; i < nc; ++i) {
+ addWatchXScreenSaver(cw[i]);
+ }
+ XFree(cw);
+ }
+
+ // now check for xscreensaver window in case it set the property
+ // before we could request property change events.
+ if (findXScreenSaver()) {
+ // found it so clear out our watch list
+ clearWatchForXScreenSaver();
+ }
+}
+
+void
+XWindowsScreenSaver::clearWatchForXScreenSaver()
+{
+ // stop watching all windows
+ XWindowsUtil::ErrorLock lock(m_display);
+ for (WatchList::iterator index = m_watchWindows.begin();
+ index != m_watchWindows.end(); ++index) {
+ XSelectInput(m_display, index->first, index->second);
+ }
+ m_watchWindows.clear();
+}
+
+void
+XWindowsScreenSaver::addWatchXScreenSaver(Window window)
+{
+ // get window attributes
+ bool error = false;
+ XWindowAttributes attr;
+ {
+ XWindowsUtil::ErrorLock lock(m_display, &error);
+ XGetWindowAttributes(m_display, window, &attr);
+ }
+
+ // if successful and window uses override_redirect (like xscreensaver
+ // does) then watch it for property changes.
+ if (!error && attr.override_redirect == True) {
+ error = false;
+ {
+ XWindowsUtil::ErrorLock lock(m_display, &error);
+ XSelectInput(m_display, window,
+ attr.your_event_mask | PropertyChangeMask);
+ }
+ if (!error) {
+ // if successful then add the window to our list
+ m_watchWindows.insert(std::make_pair(window, attr.your_event_mask));
+ }
+ }
+}
+
+void
+XWindowsScreenSaver::updateDisableTimer()
+{
+ if (m_disabled && !m_suppressDisable && m_disableTimer == NULL) {
+ // 5 seconds should be plenty often to suppress the screen saver
+ m_disableTimer = m_events->newTimer(5.0, this);
+ }
+ else if ((!m_disabled || m_suppressDisable) && m_disableTimer != NULL) {
+ m_events->deleteTimer(m_disableTimer);
+ m_disableTimer = NULL;
+ }
+}
+
+void
+XWindowsScreenSaver::handleDisableTimer(const Event&, void*)
+{
+ // send fake mouse motion directly to xscreensaver
+ if (m_xscreensaver != None) {
+ XEvent event;
+ event.xmotion.type = MotionNotify;
+ event.xmotion.display = m_display;
+ event.xmotion.window = m_xscreensaver;
+ event.xmotion.root = DefaultRootWindow(m_display);
+ event.xmotion.subwindow = None;
+ event.xmotion.time = CurrentTime;
+ event.xmotion.x = m_disablePos;
+ event.xmotion.y = 0;
+ event.xmotion.x_root = m_disablePos;
+ event.xmotion.y_root = 0;
+ event.xmotion.state = 0;
+ event.xmotion.is_hint = NotifyNormal;
+ event.xmotion.same_screen = True;
+
+ XWindowsUtil::ErrorLock lock(m_display);
+ XSendEvent(m_display, m_xscreensaver, False, 0, &event);
+
+ m_disablePos = 20 - m_disablePos;
+ }
+}
+
+void
+XWindowsScreenSaver::activateDPMS(bool activate)
+{
+#if HAVE_X11_EXTENSIONS_DPMS_H
+ if (m_dpms) {
+ // DPMSForceLevel will generate a BadMatch if DPMS is disabled
+ XWindowsUtil::ErrorLock lock(m_display);
+ DPMSForceLevel(m_display, activate ? DPMSModeStandby : DPMSModeOn);
+ }
+#endif
+}
+
+void
+XWindowsScreenSaver::enableDPMS(bool enable)
+{
+#if HAVE_X11_EXTENSIONS_DPMS_H
+ if (m_dpms) {
+ if (enable) {
+ DPMSEnable(m_display);
+ }
+ else {
+ DPMSDisable(m_display);
+ }
+ }
+#endif
+}
+
+bool
+XWindowsScreenSaver::isDPMSEnabled() const
+{
+#if HAVE_X11_EXTENSIONS_DPMS_H
+ if (m_dpms) {
+ CARD16 level;
+ BOOL state;
+ DPMSInfo(m_display, &level, &state);
+ return (state != False);
+ }
+ else {
+ return false;
+ }
+#else
+ return false;
+#endif
+}
+
+bool
+XWindowsScreenSaver::isDPMSActivated() const
+{
+#if HAVE_X11_EXTENSIONS_DPMS_H
+ if (m_dpms) {
+ CARD16 level;
+ BOOL state;
+ DPMSInfo(m_display, &level, &state);
+ return (level != DPMSModeOn);
+ }
+ else {
+ return false;
+ }
+#else
+ return false;
+#endif
+}
diff --git a/src/lib/platform/XWindowsScreenSaver.h b/src/lib/platform/XWindowsScreenSaver.h
new file mode 100644
index 0000000..db85f41
--- /dev/null
+++ b/src/lib/platform/XWindowsScreenSaver.h
@@ -0,0 +1,169 @@
+/*
+ * barrier -- 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 "barrier/IScreenSaver.h"
+#include "base/IEventQueue.h"
+#include "common/stdmap.h"
+
+#if X_DISPLAY_MISSING
+# error X11 is required to build barrier
+#else
+# include <X11/Xlib.h>
+#endif
+
+class Event;
+class EventQueueTimer;
+
+//! X11 screen saver implementation
+class XWindowsScreenSaver : public IScreenSaver {
+public:
+ XWindowsScreenSaver(Display*, Window, void* eventTarget, IEventQueue* events);
+ virtual ~XWindowsScreenSaver();
+
+ //! @name manipulators
+ //@{
+
+ //! Event filtering
+ /*!
+ Should be called for each system event before event translation and
+ dispatch. Returns true to skip translation and dispatch.
+ */
+ bool handleXEvent(const XEvent*);
+
+ //! Destroy without the display
+ /*!
+ Tells this object to delete itself without using the X11 display.
+ It may leak some resources as a result.
+ */
+ void destroy();
+
+ //@}
+
+ // IScreenSaver overrides
+ virtual void enable();
+ virtual void disable();
+ virtual void activate();
+ virtual void deactivate();
+ virtual bool isActive() const;
+
+private:
+ // find and set the running xscreensaver's window. returns true iff
+ // found.
+ bool findXScreenSaver();
+
+ // set the xscreensaver's window, updating the activation state flag
+ void setXScreenSaver(Window);
+
+ // returns true if the window appears to be the xscreensaver window
+ bool isXScreenSaver(Window) const;
+
+ // set xscreensaver's activation state flag. sends notification
+ // if the state has changed.
+ void setXScreenSaverActive(bool activated);
+
+ // send a command to xscreensaver
+ void sendXScreenSaverCommand(Atom, long = 0, long = 0);
+
+ // watch all windows that could potentially be the xscreensaver for
+ // the events that will confirm it.
+ void watchForXScreenSaver();
+
+ // stop watching all watched windows
+ void clearWatchForXScreenSaver();
+
+ // add window to the watch list
+ void addWatchXScreenSaver(Window window);
+
+ // install/uninstall the job used to suppress the screensaver
+ void updateDisableTimer();
+
+ // called periodically to prevent the screen saver from starting
+ void handleDisableTimer(const Event&, void*);
+
+ // force DPMS to activate or deactivate
+ void activateDPMS(bool activate);
+
+ // enable/disable DPMS screen saver
+ void enableDPMS(bool);
+
+ // check if DPMS is enabled
+ bool isDPMSEnabled() const;
+
+ // check if DPMS is activate
+ bool isDPMSActivated() const;
+
+private:
+ typedef std::map<Window, long> WatchList;
+
+ // the X display
+ Display* m_display;
+
+ // window to receive xscreensaver repsonses
+ Window m_xscreensaverSink;
+
+ // the target for the events we generate
+ void* m_eventTarget;
+
+ // xscreensaver's window
+ Window m_xscreensaver;
+
+ // xscreensaver activation state
+ bool m_xscreensaverActive;
+
+ // old event mask on root window
+ long m_rootEventMask;
+
+ // potential xscreensaver windows being watched
+ WatchList m_watchWindows;
+
+ // atoms used to communicate with xscreensaver's window
+ Atom m_atomScreenSaver;
+ Atom m_atomScreenSaverVersion;
+ Atom m_atomScreenSaverActivate;
+ Atom m_atomScreenSaverDeactivate;
+
+ // built-in screen saver settings
+ int m_timeout;
+ int m_interval;
+ int m_preferBlanking;
+ int m_allowExposures;
+
+ // DPMS screen saver settings
+ bool m_dpms;
+ bool m_dpmsEnabled;
+
+ // true iff the client wants the screen saver suppressed
+ bool m_disabled;
+
+ // true iff we're ignoring m_disabled. this is true, for example,
+ // when the client has called activate() and so presumably wants
+ // to activate the screen saver even if disabled.
+ bool m_suppressDisable;
+
+ // the disable timer (NULL if not installed)
+ EventQueueTimer* m_disableTimer;
+
+ // fake mouse motion position for suppressing the screen saver.
+ // xscreensaver since 2.21 requires the mouse to move more than 10
+ // pixels to be considered significant.
+ SInt32 m_disablePos;
+
+ IEventQueue* m_events;
+};
diff --git a/src/lib/platform/XWindowsUtil.cpp b/src/lib/platform/XWindowsUtil.cpp
new file mode 100644
index 0000000..65448e8
--- /dev/null
+++ b/src/lib/platform/XWindowsUtil.cpp
@@ -0,0 +1,1790 @@
+/*
+ * barrier -- 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 "platform/XWindowsUtil.h"
+
+#include "barrier/key_types.h"
+#include "mt/Thread.h"
+#include "base/Log.h"
+#include "base/String.h"
+
+#include <X11/Xatom.h>
+#define XK_APL
+#define XK_ARABIC
+#define XK_ARMENIAN
+#define XK_CAUCASUS
+#define XK_CURRENCY
+#define XK_CYRILLIC
+#define XK_GEORGIAN
+#define XK_GREEK
+#define XK_HEBREW
+#define XK_KATAKANA
+#define XK_KOREAN
+#define XK_LATIN1
+#define XK_LATIN2
+#define XK_LATIN3
+#define XK_LATIN4
+#define XK_LATIN8
+#define XK_LATIN9
+#define XK_MISCELLANY
+#define XK_PUBLISHING
+#define XK_SPECIAL
+#define XK_TECHNICAL
+#define XK_THAI
+#define XK_VIETNAMESE
+#define XK_XKB_KEYS
+#include <X11/keysym.h>
+
+#if !defined(XK_OE)
+#define XK_OE 0x13bc
+#endif
+#if !defined(XK_oe)
+#define XK_oe 0x13bd
+#endif
+#if !defined(XK_Ydiaeresis)
+#define XK_Ydiaeresis 0x13be
+#endif
+
+/*
+ * This table maps keysym values into the corresponding ISO 10646
+ * (UCS, Unicode) values.
+ *
+ * The array keysymtab[] contains pairs of X11 keysym values for graphical
+ * characters and the corresponding Unicode value.
+ *
+ * Author: Markus G. Kuhn <http://www.cl.cam.ac.uk/~mgk25/>,
+ * University of Cambridge, April 2001
+ *
+ * Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing
+ * an initial draft of the mapping table.
+ *
+ * This software is in the public domain. Share and enjoy!
+ */
+
+struct codepair {
+ KeySym keysym;
+ UInt32 ucs4;
+} s_keymap[] = {
+{ XK_Aogonek, 0x0104 }, /* LATIN CAPITAL LETTER A WITH OGONEK */
+{ XK_breve, 0x02d8 }, /* BREVE */
+{ XK_Lstroke, 0x0141 }, /* LATIN CAPITAL LETTER L WITH STROKE */
+{ XK_Lcaron, 0x013d }, /* LATIN CAPITAL LETTER L WITH CARON */
+{ XK_Sacute, 0x015a }, /* LATIN CAPITAL LETTER S WITH ACUTE */
+{ XK_Scaron, 0x0160 }, /* LATIN CAPITAL LETTER S WITH CARON */
+{ XK_Scedilla, 0x015e }, /* LATIN CAPITAL LETTER S WITH CEDILLA */
+{ XK_Tcaron, 0x0164 }, /* LATIN CAPITAL LETTER T WITH CARON */
+{ XK_Zacute, 0x0179 }, /* LATIN CAPITAL LETTER Z WITH ACUTE */
+{ XK_Zcaron, 0x017d }, /* LATIN CAPITAL LETTER Z WITH CARON */
+{ XK_Zabovedot, 0x017b }, /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */
+{ XK_aogonek, 0x0105 }, /* LATIN SMALL LETTER A WITH OGONEK */
+{ XK_ogonek, 0x02db }, /* OGONEK */
+{ XK_lstroke, 0x0142 }, /* LATIN SMALL LETTER L WITH STROKE */
+{ XK_lcaron, 0x013e }, /* LATIN SMALL LETTER L WITH CARON */
+{ XK_sacute, 0x015b }, /* LATIN SMALL LETTER S WITH ACUTE */
+{ XK_caron, 0x02c7 }, /* CARON */
+{ XK_scaron, 0x0161 }, /* LATIN SMALL LETTER S WITH CARON */
+{ XK_scedilla, 0x015f }, /* LATIN SMALL LETTER S WITH CEDILLA */
+{ XK_tcaron, 0x0165 }, /* LATIN SMALL LETTER T WITH CARON */
+{ XK_zacute, 0x017a }, /* LATIN SMALL LETTER Z WITH ACUTE */
+{ XK_doubleacute, 0x02dd }, /* DOUBLE ACUTE ACCENT */
+{ XK_zcaron, 0x017e }, /* LATIN SMALL LETTER Z WITH CARON */
+{ XK_zabovedot, 0x017c }, /* LATIN SMALL LETTER Z WITH DOT ABOVE */
+{ XK_Racute, 0x0154 }, /* LATIN CAPITAL LETTER R WITH ACUTE */
+{ XK_Abreve, 0x0102 }, /* LATIN CAPITAL LETTER A WITH BREVE */
+{ XK_Lacute, 0x0139 }, /* LATIN CAPITAL LETTER L WITH ACUTE */
+{ XK_Cacute, 0x0106 }, /* LATIN CAPITAL LETTER C WITH ACUTE */
+{ XK_Ccaron, 0x010c }, /* LATIN CAPITAL LETTER C WITH CARON */
+{ XK_Eogonek, 0x0118 }, /* LATIN CAPITAL LETTER E WITH OGONEK */
+{ XK_Ecaron, 0x011a }, /* LATIN CAPITAL LETTER E WITH CARON */
+{ XK_Dcaron, 0x010e }, /* LATIN CAPITAL LETTER D WITH CARON */
+{ XK_Dstroke, 0x0110 }, /* LATIN CAPITAL LETTER D WITH STROKE */
+{ XK_Nacute, 0x0143 }, /* LATIN CAPITAL LETTER N WITH ACUTE */
+{ XK_Ncaron, 0x0147 }, /* LATIN CAPITAL LETTER N WITH CARON */
+{ XK_Odoubleacute, 0x0150 }, /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
+{ XK_Rcaron, 0x0158 }, /* LATIN CAPITAL LETTER R WITH CARON */
+{ XK_Uring, 0x016e }, /* LATIN CAPITAL LETTER U WITH RING ABOVE */
+{ XK_Udoubleacute, 0x0170 }, /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
+{ XK_Tcedilla, 0x0162 }, /* LATIN CAPITAL LETTER T WITH CEDILLA */
+{ XK_racute, 0x0155 }, /* LATIN SMALL LETTER R WITH ACUTE */
+{ XK_abreve, 0x0103 }, /* LATIN SMALL LETTER A WITH BREVE */
+{ XK_lacute, 0x013a }, /* LATIN SMALL LETTER L WITH ACUTE */
+{ XK_cacute, 0x0107 }, /* LATIN SMALL LETTER C WITH ACUTE */
+{ XK_ccaron, 0x010d }, /* LATIN SMALL LETTER C WITH CARON */
+{ XK_eogonek, 0x0119 }, /* LATIN SMALL LETTER E WITH OGONEK */
+{ XK_ecaron, 0x011b }, /* LATIN SMALL LETTER E WITH CARON */
+{ XK_dcaron, 0x010f }, /* LATIN SMALL LETTER D WITH CARON */
+{ XK_dstroke, 0x0111 }, /* LATIN SMALL LETTER D WITH STROKE */
+{ XK_nacute, 0x0144 }, /* LATIN SMALL LETTER N WITH ACUTE */
+{ XK_ncaron, 0x0148 }, /* LATIN SMALL LETTER N WITH CARON */
+{ XK_odoubleacute, 0x0151 }, /* LATIN SMALL LETTER O WITH DOUBLE ACUTE */
+{ XK_rcaron, 0x0159 }, /* LATIN SMALL LETTER R WITH CARON */
+{ XK_uring, 0x016f }, /* LATIN SMALL LETTER U WITH RING ABOVE */
+{ XK_udoubleacute, 0x0171 }, /* LATIN SMALL LETTER U WITH DOUBLE ACUTE */
+{ XK_tcedilla, 0x0163 }, /* LATIN SMALL LETTER T WITH CEDILLA */
+{ XK_abovedot, 0x02d9 }, /* DOT ABOVE */
+{ XK_Hstroke, 0x0126 }, /* LATIN CAPITAL LETTER H WITH STROKE */
+{ XK_Hcircumflex, 0x0124 }, /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
+{ XK_Iabovedot, 0x0130 }, /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
+{ XK_Gbreve, 0x011e }, /* LATIN CAPITAL LETTER G WITH BREVE */
+{ XK_Jcircumflex, 0x0134 }, /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
+{ XK_hstroke, 0x0127 }, /* LATIN SMALL LETTER H WITH STROKE */
+{ XK_hcircumflex, 0x0125 }, /* LATIN SMALL LETTER H WITH CIRCUMFLEX */
+{ XK_idotless, 0x0131 }, /* LATIN SMALL LETTER DOTLESS I */
+{ XK_gbreve, 0x011f }, /* LATIN SMALL LETTER G WITH BREVE */
+{ XK_jcircumflex, 0x0135 }, /* LATIN SMALL LETTER J WITH CIRCUMFLEX */
+{ XK_Cabovedot, 0x010a }, /* LATIN CAPITAL LETTER C WITH DOT ABOVE */
+{ XK_Ccircumflex, 0x0108 }, /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
+{ XK_Gabovedot, 0x0120 }, /* LATIN CAPITAL LETTER G WITH DOT ABOVE */
+{ XK_Gcircumflex, 0x011c }, /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
+{ XK_Ubreve, 0x016c }, /* LATIN CAPITAL LETTER U WITH BREVE */
+{ XK_Scircumflex, 0x015c }, /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
+{ XK_cabovedot, 0x010b }, /* LATIN SMALL LETTER C WITH DOT ABOVE */
+{ XK_ccircumflex, 0x0109 }, /* LATIN SMALL LETTER C WITH CIRCUMFLEX */
+{ XK_gabovedot, 0x0121 }, /* LATIN SMALL LETTER G WITH DOT ABOVE */
+{ XK_gcircumflex, 0x011d }, /* LATIN SMALL LETTER G WITH CIRCUMFLEX */
+{ XK_ubreve, 0x016d }, /* LATIN SMALL LETTER U WITH BREVE */
+{ XK_scircumflex, 0x015d }, /* LATIN SMALL LETTER S WITH CIRCUMFLEX */
+{ XK_kra, 0x0138 }, /* LATIN SMALL LETTER KRA */
+{ XK_Rcedilla, 0x0156 }, /* LATIN CAPITAL LETTER R WITH CEDILLA */
+{ XK_Itilde, 0x0128 }, /* LATIN CAPITAL LETTER I WITH TILDE */
+{ XK_Lcedilla, 0x013b }, /* LATIN CAPITAL LETTER L WITH CEDILLA */
+{ XK_Emacron, 0x0112 }, /* LATIN CAPITAL LETTER E WITH MACRON */
+{ XK_Gcedilla, 0x0122 }, /* LATIN CAPITAL LETTER G WITH CEDILLA */
+{ XK_Tslash, 0x0166 }, /* LATIN CAPITAL LETTER T WITH STROKE */
+{ XK_rcedilla, 0x0157 }, /* LATIN SMALL LETTER R WITH CEDILLA */
+{ XK_itilde, 0x0129 }, /* LATIN SMALL LETTER I WITH TILDE */
+{ XK_lcedilla, 0x013c }, /* LATIN SMALL LETTER L WITH CEDILLA */
+{ XK_emacron, 0x0113 }, /* LATIN SMALL LETTER E WITH MACRON */
+{ XK_gcedilla, 0x0123 }, /* LATIN SMALL LETTER G WITH CEDILLA */
+{ XK_tslash, 0x0167 }, /* LATIN SMALL LETTER T WITH STROKE */
+{ XK_ENG, 0x014a }, /* LATIN CAPITAL LETTER ENG */
+{ XK_eng, 0x014b }, /* LATIN SMALL LETTER ENG */
+{ XK_Amacron, 0x0100 }, /* LATIN CAPITAL LETTER A WITH MACRON */
+{ XK_Iogonek, 0x012e }, /* LATIN CAPITAL LETTER I WITH OGONEK */
+{ XK_Eabovedot, 0x0116 }, /* LATIN CAPITAL LETTER E WITH DOT ABOVE */
+{ XK_Imacron, 0x012a }, /* LATIN CAPITAL LETTER I WITH MACRON */
+{ XK_Ncedilla, 0x0145 }, /* LATIN CAPITAL LETTER N WITH CEDILLA */
+{ XK_Omacron, 0x014c }, /* LATIN CAPITAL LETTER O WITH MACRON */
+{ XK_Kcedilla, 0x0136 }, /* LATIN CAPITAL LETTER K WITH CEDILLA */
+{ XK_Uogonek, 0x0172 }, /* LATIN CAPITAL LETTER U WITH OGONEK */
+{ XK_Utilde, 0x0168 }, /* LATIN CAPITAL LETTER U WITH TILDE */
+{ XK_Umacron, 0x016a }, /* LATIN CAPITAL LETTER U WITH MACRON */
+{ XK_amacron, 0x0101 }, /* LATIN SMALL LETTER A WITH MACRON */
+{ XK_iogonek, 0x012f }, /* LATIN SMALL LETTER I WITH OGONEK */
+{ XK_eabovedot, 0x0117 }, /* LATIN SMALL LETTER E WITH DOT ABOVE */
+{ XK_imacron, 0x012b }, /* LATIN SMALL LETTER I WITH MACRON */
+{ XK_ncedilla, 0x0146 }, /* LATIN SMALL LETTER N WITH CEDILLA */
+{ XK_omacron, 0x014d }, /* LATIN SMALL LETTER O WITH MACRON */
+{ XK_kcedilla, 0x0137 }, /* LATIN SMALL LETTER K WITH CEDILLA */
+{ XK_uogonek, 0x0173 }, /* LATIN SMALL LETTER U WITH OGONEK */
+{ XK_utilde, 0x0169 }, /* LATIN SMALL LETTER U WITH TILDE */
+{ XK_umacron, 0x016b }, /* LATIN SMALL LETTER U WITH MACRON */
+#if defined(XK_Babovedot)
+{ XK_Babovedot, 0x1e02 }, /* LATIN CAPITAL LETTER B WITH DOT ABOVE */
+{ XK_babovedot, 0x1e03 }, /* LATIN SMALL LETTER B WITH DOT ABOVE */
+{ XK_Dabovedot, 0x1e0a }, /* LATIN CAPITAL LETTER D WITH DOT ABOVE */
+{ XK_Wgrave, 0x1e80 }, /* LATIN CAPITAL LETTER W WITH GRAVE */
+{ XK_Wacute, 0x1e82 }, /* LATIN CAPITAL LETTER W WITH ACUTE */
+{ XK_dabovedot, 0x1e0b }, /* LATIN SMALL LETTER D WITH DOT ABOVE */
+{ XK_Ygrave, 0x1ef2 }, /* LATIN CAPITAL LETTER Y WITH GRAVE */
+{ XK_Fabovedot, 0x1e1e }, /* LATIN CAPITAL LETTER F WITH DOT ABOVE */
+{ XK_fabovedot, 0x1e1f }, /* LATIN SMALL LETTER F WITH DOT ABOVE */
+{ XK_Mabovedot, 0x1e40 }, /* LATIN CAPITAL LETTER M WITH DOT ABOVE */
+{ XK_mabovedot, 0x1e41 }, /* LATIN SMALL LETTER M WITH DOT ABOVE */
+{ XK_Pabovedot, 0x1e56 }, /* LATIN CAPITAL LETTER P WITH DOT ABOVE */
+{ XK_wgrave, 0x1e81 }, /* LATIN SMALL LETTER W WITH GRAVE */
+{ XK_pabovedot, 0x1e57 }, /* LATIN SMALL LETTER P WITH DOT ABOVE */
+{ XK_wacute, 0x1e83 }, /* LATIN SMALL LETTER W WITH ACUTE */
+{ XK_Sabovedot, 0x1e60 }, /* LATIN CAPITAL LETTER S WITH DOT ABOVE */
+{ XK_ygrave, 0x1ef3 }, /* LATIN SMALL LETTER Y WITH GRAVE */
+{ XK_Wdiaeresis, 0x1e84 }, /* LATIN CAPITAL LETTER W WITH DIAERESIS */
+{ XK_wdiaeresis, 0x1e85 }, /* LATIN SMALL LETTER W WITH DIAERESIS */
+{ XK_sabovedot, 0x1e61 }, /* LATIN SMALL LETTER S WITH DOT ABOVE */
+{ XK_Wcircumflex, 0x0174 }, /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */
+{ XK_Tabovedot, 0x1e6a }, /* LATIN CAPITAL LETTER T WITH DOT ABOVE */
+{ XK_Ycircumflex, 0x0176 }, /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */
+{ XK_wcircumflex, 0x0175 }, /* LATIN SMALL LETTER W WITH CIRCUMFLEX */
+{ XK_tabovedot, 0x1e6b }, /* LATIN SMALL LETTER T WITH DOT ABOVE */
+{ XK_ycircumflex, 0x0177 }, /* LATIN SMALL LETTER Y WITH CIRCUMFLEX */
+#endif // defined(XK_Babovedot)
+#if defined(XK_overline)
+{ XK_overline, 0x203e }, /* OVERLINE */
+{ XK_kana_fullstop, 0x3002 }, /* IDEOGRAPHIC FULL STOP */
+{ XK_kana_openingbracket, 0x300c }, /* LEFT CORNER BRACKET */
+{ XK_kana_closingbracket, 0x300d }, /* RIGHT CORNER BRACKET */
+{ XK_kana_comma, 0x3001 }, /* IDEOGRAPHIC COMMA */
+{ XK_kana_conjunctive, 0x30fb }, /* KATAKANA MIDDLE DOT */
+{ XK_kana_WO, 0x30f2 }, /* KATAKANA LETTER WO */
+{ XK_kana_a, 0x30a1 }, /* KATAKANA LETTER SMALL A */
+{ XK_kana_i, 0x30a3 }, /* KATAKANA LETTER SMALL I */
+{ XK_kana_u, 0x30a5 }, /* KATAKANA LETTER SMALL U */
+{ XK_kana_e, 0x30a7 }, /* KATAKANA LETTER SMALL E */
+{ XK_kana_o, 0x30a9 }, /* KATAKANA LETTER SMALL O */
+{ XK_kana_ya, 0x30e3 }, /* KATAKANA LETTER SMALL YA */
+{ XK_kana_yu, 0x30e5 }, /* KATAKANA LETTER SMALL YU */
+{ XK_kana_yo, 0x30e7 }, /* KATAKANA LETTER SMALL YO */
+{ XK_kana_tsu, 0x30c3 }, /* KATAKANA LETTER SMALL TU */
+{ XK_prolongedsound, 0x30fc }, /* KATAKANA-HIRAGANA PROLONGED SOUND MARK */
+{ XK_kana_A, 0x30a2 }, /* KATAKANA LETTER A */
+{ XK_kana_I, 0x30a4 }, /* KATAKANA LETTER I */
+{ XK_kana_U, 0x30a6 }, /* KATAKANA LETTER U */
+{ XK_kana_E, 0x30a8 }, /* KATAKANA LETTER E */
+{ XK_kana_O, 0x30aa }, /* KATAKANA LETTER O */
+{ XK_kana_KA, 0x30ab }, /* KATAKANA LETTER KA */
+{ XK_kana_KI, 0x30ad }, /* KATAKANA LETTER KI */
+{ XK_kana_KU, 0x30af }, /* KATAKANA LETTER KU */
+{ XK_kana_KE, 0x30b1 }, /* KATAKANA LETTER KE */
+{ XK_kana_KO, 0x30b3 }, /* KATAKANA LETTER KO */
+{ XK_kana_SA, 0x30b5 }, /* KATAKANA LETTER SA */
+{ XK_kana_SHI, 0x30b7 }, /* KATAKANA LETTER SI */
+{ XK_kana_SU, 0x30b9 }, /* KATAKANA LETTER SU */
+{ XK_kana_SE, 0x30bb }, /* KATAKANA LETTER SE */
+{ XK_kana_SO, 0x30bd }, /* KATAKANA LETTER SO */
+{ XK_kana_TA, 0x30bf }, /* KATAKANA LETTER TA */
+{ XK_kana_CHI, 0x30c1 }, /* KATAKANA LETTER TI */
+{ XK_kana_TSU, 0x30c4 }, /* KATAKANA LETTER TU */
+{ XK_kana_TE, 0x30c6 }, /* KATAKANA LETTER TE */
+{ XK_kana_TO, 0x30c8 }, /* KATAKANA LETTER TO */
+{ XK_kana_NA, 0x30ca }, /* KATAKANA LETTER NA */
+{ XK_kana_NI, 0x30cb }, /* KATAKANA LETTER NI */
+{ XK_kana_NU, 0x30cc }, /* KATAKANA LETTER NU */
+{ XK_kana_NE, 0x30cd }, /* KATAKANA LETTER NE */
+{ XK_kana_NO, 0x30ce }, /* KATAKANA LETTER NO */
+{ XK_kana_HA, 0x30cf }, /* KATAKANA LETTER HA */
+{ XK_kana_HI, 0x30d2 }, /* KATAKANA LETTER HI */
+{ XK_kana_FU, 0x30d5 }, /* KATAKANA LETTER HU */
+{ XK_kana_HE, 0x30d8 }, /* KATAKANA LETTER HE */
+{ XK_kana_HO, 0x30db }, /* KATAKANA LETTER HO */
+{ XK_kana_MA, 0x30de }, /* KATAKANA LETTER MA */
+{ XK_kana_MI, 0x30df }, /* KATAKANA LETTER MI */
+{ XK_kana_MU, 0x30e0 }, /* KATAKANA LETTER MU */
+{ XK_kana_ME, 0x30e1 }, /* KATAKANA LETTER ME */
+{ XK_kana_MO, 0x30e2 }, /* KATAKANA LETTER MO */
+{ XK_kana_YA, 0x30e4 }, /* KATAKANA LETTER YA */
+{ XK_kana_YU, 0x30e6 }, /* KATAKANA LETTER YU */
+{ XK_kana_YO, 0x30e8 }, /* KATAKANA LETTER YO */
+{ XK_kana_RA, 0x30e9 }, /* KATAKANA LETTER RA */
+{ XK_kana_RI, 0x30ea }, /* KATAKANA LETTER RI */
+{ XK_kana_RU, 0x30eb }, /* KATAKANA LETTER RU */
+{ XK_kana_RE, 0x30ec }, /* KATAKANA LETTER RE */
+{ XK_kana_RO, 0x30ed }, /* KATAKANA LETTER RO */
+{ XK_kana_WA, 0x30ef }, /* KATAKANA LETTER WA */
+{ XK_kana_N, 0x30f3 }, /* KATAKANA LETTER N */
+{ XK_voicedsound, 0x309b }, /* KATAKANA-HIRAGANA VOICED SOUND MARK */
+{ XK_semivoicedsound, 0x309c }, /* KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
+#endif // defined(XK_overline)
+#if defined(XK_Farsi_0)
+{ XK_Farsi_0, 0x06f0 }, /* EXTENDED ARABIC-INDIC DIGIT 0 */
+{ XK_Farsi_1, 0x06f1 }, /* EXTENDED ARABIC-INDIC DIGIT 1 */
+{ XK_Farsi_2, 0x06f2 }, /* EXTENDED ARABIC-INDIC DIGIT 2 */
+{ XK_Farsi_3, 0x06f3 }, /* EXTENDED ARABIC-INDIC DIGIT 3 */
+{ XK_Farsi_4, 0x06f4 }, /* EXTENDED ARABIC-INDIC DIGIT 4 */
+{ XK_Farsi_5, 0x06f5 }, /* EXTENDED ARABIC-INDIC DIGIT 5 */
+{ XK_Farsi_6, 0x06f6 }, /* EXTENDED ARABIC-INDIC DIGIT 6 */
+{ XK_Farsi_7, 0x06f7 }, /* EXTENDED ARABIC-INDIC DIGIT 7 */
+{ XK_Farsi_8, 0x06f8 }, /* EXTENDED ARABIC-INDIC DIGIT 8 */
+{ XK_Farsi_9, 0x06f9 }, /* EXTENDED ARABIC-INDIC DIGIT 9 */
+{ XK_Arabic_percent, 0x066a }, /* ARABIC PERCENT */
+{ XK_Arabic_superscript_alef, 0x0670 }, /* ARABIC LETTER SUPERSCRIPT ALEF */
+{ XK_Arabic_tteh, 0x0679 }, /* ARABIC LETTER TTEH */
+{ XK_Arabic_peh, 0x067e }, /* ARABIC LETTER PEH */
+{ XK_Arabic_tcheh, 0x0686 }, /* ARABIC LETTER TCHEH */
+{ XK_Arabic_ddal, 0x0688 }, /* ARABIC LETTER DDAL */
+{ XK_Arabic_rreh, 0x0691 }, /* ARABIC LETTER RREH */
+{ XK_Arabic_comma, 0x060c }, /* ARABIC COMMA */
+{ XK_Arabic_fullstop, 0x06d4 }, /* ARABIC FULLSTOP */
+{ XK_Arabic_semicolon, 0x061b }, /* ARABIC SEMICOLON */
+{ XK_Arabic_0, 0x0660 }, /* ARABIC 0 */
+{ XK_Arabic_1, 0x0661 }, /* ARABIC 1 */
+{ XK_Arabic_2, 0x0662 }, /* ARABIC 2 */
+{ XK_Arabic_3, 0x0663 }, /* ARABIC 3 */
+{ XK_Arabic_4, 0x0664 }, /* ARABIC 4 */
+{ XK_Arabic_5, 0x0665 }, /* ARABIC 5 */
+{ XK_Arabic_6, 0x0666 }, /* ARABIC 6 */
+{ XK_Arabic_7, 0x0667 }, /* ARABIC 7 */
+{ XK_Arabic_8, 0x0668 }, /* ARABIC 8 */
+{ XK_Arabic_9, 0x0669 }, /* ARABIC 9 */
+{ XK_Arabic_question_mark, 0x061f }, /* ARABIC QUESTION MARK */
+{ XK_Arabic_hamza, 0x0621 }, /* ARABIC LETTER HAMZA */
+{ XK_Arabic_maddaonalef, 0x0622 }, /* ARABIC LETTER ALEF WITH MADDA ABOVE */
+{ XK_Arabic_hamzaonalef, 0x0623 }, /* ARABIC LETTER ALEF WITH HAMZA ABOVE */
+{ XK_Arabic_hamzaonwaw, 0x0624 }, /* ARABIC LETTER WAW WITH HAMZA ABOVE */
+{ XK_Arabic_hamzaunderalef, 0x0625 }, /* ARABIC LETTER ALEF WITH HAMZA BELOW */
+{ XK_Arabic_hamzaonyeh, 0x0626 }, /* ARABIC LETTER YEH WITH HAMZA ABOVE */
+{ XK_Arabic_alef, 0x0627 }, /* ARABIC LETTER ALEF */
+{ XK_Arabic_beh, 0x0628 }, /* ARABIC LETTER BEH */
+{ XK_Arabic_tehmarbuta, 0x0629 }, /* ARABIC LETTER TEH MARBUTA */
+{ XK_Arabic_teh, 0x062a }, /* ARABIC LETTER TEH */
+{ XK_Arabic_theh, 0x062b }, /* ARABIC LETTER THEH */
+{ XK_Arabic_jeem, 0x062c }, /* ARABIC LETTER JEEM */
+{ XK_Arabic_hah, 0x062d }, /* ARABIC LETTER HAH */
+{ XK_Arabic_khah, 0x062e }, /* ARABIC LETTER KHAH */
+{ XK_Arabic_dal, 0x062f }, /* ARABIC LETTER DAL */
+{ XK_Arabic_thal, 0x0630 }, /* ARABIC LETTER THAL */
+{ XK_Arabic_ra, 0x0631 }, /* ARABIC LETTER REH */
+{ XK_Arabic_zain, 0x0632 }, /* ARABIC LETTER ZAIN */
+{ XK_Arabic_seen, 0x0633 }, /* ARABIC LETTER SEEN */
+{ XK_Arabic_sheen, 0x0634 }, /* ARABIC LETTER SHEEN */
+{ XK_Arabic_sad, 0x0635 }, /* ARABIC LETTER SAD */
+{ XK_Arabic_dad, 0x0636 }, /* ARABIC LETTER DAD */
+{ XK_Arabic_tah, 0x0637 }, /* ARABIC LETTER TAH */
+{ XK_Arabic_zah, 0x0638 }, /* ARABIC LETTER ZAH */
+{ XK_Arabic_ain, 0x0639 }, /* ARABIC LETTER AIN */
+{ XK_Arabic_ghain, 0x063a }, /* ARABIC LETTER GHAIN */
+{ XK_Arabic_tatweel, 0x0640 }, /* ARABIC TATWEEL */
+{ XK_Arabic_feh, 0x0641 }, /* ARABIC LETTER FEH */
+{ XK_Arabic_qaf, 0x0642 }, /* ARABIC LETTER QAF */
+{ XK_Arabic_kaf, 0x0643 }, /* ARABIC LETTER KAF */
+{ XK_Arabic_lam, 0x0644 }, /* ARABIC LETTER LAM */
+{ XK_Arabic_meem, 0x0645 }, /* ARABIC LETTER MEEM */
+{ XK_Arabic_noon, 0x0646 }, /* ARABIC LETTER NOON */
+{ XK_Arabic_ha, 0x0647 }, /* ARABIC LETTER HEH */
+{ XK_Arabic_waw, 0x0648 }, /* ARABIC LETTER WAW */
+{ XK_Arabic_alefmaksura, 0x0649 }, /* ARABIC LETTER ALEF MAKSURA */
+{ XK_Arabic_yeh, 0x064a }, /* ARABIC LETTER YEH */
+{ XK_Arabic_fathatan, 0x064b }, /* ARABIC FATHATAN */
+{ XK_Arabic_dammatan, 0x064c }, /* ARABIC DAMMATAN */
+{ XK_Arabic_kasratan, 0x064d }, /* ARABIC KASRATAN */
+{ XK_Arabic_fatha, 0x064e }, /* ARABIC FATHA */
+{ XK_Arabic_damma, 0x064f }, /* ARABIC DAMMA */
+{ XK_Arabic_kasra, 0x0650 }, /* ARABIC KASRA */
+{ XK_Arabic_shadda, 0x0651 }, /* ARABIC SHADDA */
+{ XK_Arabic_sukun, 0x0652 }, /* ARABIC SUKUN */
+{ XK_Arabic_madda_above, 0x0653 }, /* ARABIC MADDA ABOVE */
+{ XK_Arabic_hamza_above, 0x0654 }, /* ARABIC HAMZA ABOVE */
+{ XK_Arabic_hamza_below, 0x0655 }, /* ARABIC HAMZA BELOW */
+{ XK_Arabic_jeh, 0x0698 }, /* ARABIC LETTER JEH */
+{ XK_Arabic_veh, 0x06a4 }, /* ARABIC LETTER VEH */
+{ XK_Arabic_keheh, 0x06a9 }, /* ARABIC LETTER KEHEH */
+{ XK_Arabic_gaf, 0x06af }, /* ARABIC LETTER GAF */
+{ XK_Arabic_noon_ghunna, 0x06ba }, /* ARABIC LETTER NOON GHUNNA */
+{ XK_Arabic_heh_doachashmee, 0x06be }, /* ARABIC LETTER HEH DOACHASHMEE */
+{ XK_Arabic_farsi_yeh, 0x06cc }, /* ARABIC LETTER FARSI YEH */
+{ XK_Arabic_yeh_baree, 0x06d2 }, /* ARABIC LETTER YEH BAREE */
+{ XK_Arabic_heh_goal, 0x06c1 }, /* ARABIC LETTER HEH GOAL */
+#endif // defined(XK_Farsi_0)
+#if defined(XK_Serbian_dje)
+{ XK_Serbian_dje, 0x0452 }, /* CYRILLIC SMALL LETTER DJE */
+{ XK_Macedonia_gje, 0x0453 }, /* CYRILLIC SMALL LETTER GJE */
+{ XK_Cyrillic_io, 0x0451 }, /* CYRILLIC SMALL LETTER IO */
+{ XK_Ukrainian_ie, 0x0454 }, /* CYRILLIC SMALL LETTER UKRAINIAN IE */
+{ XK_Macedonia_dse, 0x0455 }, /* CYRILLIC SMALL LETTER DZE */
+{ XK_Ukrainian_i, 0x0456 }, /* CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
+{ XK_Ukrainian_yi, 0x0457 }, /* CYRILLIC SMALL LETTER YI */
+{ XK_Cyrillic_je, 0x0458 }, /* CYRILLIC SMALL LETTER JE */
+{ XK_Cyrillic_lje, 0x0459 }, /* CYRILLIC SMALL LETTER LJE */
+{ XK_Cyrillic_nje, 0x045a }, /* CYRILLIC SMALL LETTER NJE */
+{ XK_Serbian_tshe, 0x045b }, /* CYRILLIC SMALL LETTER TSHE */
+{ XK_Macedonia_kje, 0x045c }, /* CYRILLIC SMALL LETTER KJE */
+#if defined(XK_Ukrainian_ghe_with_upturn)
+{ XK_Ukrainian_ghe_with_upturn, 0x0491 }, /* CYRILLIC SMALL LETTER GHE WITH UPTURN */
+#endif
+{ XK_Byelorussian_shortu, 0x045e }, /* CYRILLIC SMALL LETTER SHORT U */
+{ XK_Cyrillic_dzhe, 0x045f }, /* CYRILLIC SMALL LETTER DZHE */
+{ XK_numerosign, 0x2116 }, /* NUMERO SIGN */
+{ XK_Serbian_DJE, 0x0402 }, /* CYRILLIC CAPITAL LETTER DJE */
+{ XK_Macedonia_GJE, 0x0403 }, /* CYRILLIC CAPITAL LETTER GJE */
+{ XK_Cyrillic_IO, 0x0401 }, /* CYRILLIC CAPITAL LETTER IO */
+{ XK_Ukrainian_IE, 0x0404 }, /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */
+{ XK_Macedonia_DSE, 0x0405 }, /* CYRILLIC CAPITAL LETTER DZE */
+{ XK_Ukrainian_I, 0x0406 }, /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
+{ XK_Ukrainian_YI, 0x0407 }, /* CYRILLIC CAPITAL LETTER YI */
+{ XK_Cyrillic_JE, 0x0408 }, /* CYRILLIC CAPITAL LETTER JE */
+{ XK_Cyrillic_LJE, 0x0409 }, /* CYRILLIC CAPITAL LETTER LJE */
+{ XK_Cyrillic_NJE, 0x040a }, /* CYRILLIC CAPITAL LETTER NJE */
+{ XK_Serbian_TSHE, 0x040b }, /* CYRILLIC CAPITAL LETTER TSHE */
+{ XK_Macedonia_KJE, 0x040c }, /* CYRILLIC CAPITAL LETTER KJE */
+#if defined(XK_Ukrainian_GHE_WITH_UPTURN)
+{ XK_Ukrainian_GHE_WITH_UPTURN, 0x0490 }, /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */
+#endif
+{ XK_Byelorussian_SHORTU, 0x040e }, /* CYRILLIC CAPITAL LETTER SHORT U */
+{ XK_Cyrillic_DZHE, 0x040f }, /* CYRILLIC CAPITAL LETTER DZHE */
+{ XK_Cyrillic_yu, 0x044e }, /* CYRILLIC SMALL LETTER YU */
+{ XK_Cyrillic_a, 0x0430 }, /* CYRILLIC SMALL LETTER A */
+{ XK_Cyrillic_be, 0x0431 }, /* CYRILLIC SMALL LETTER BE */
+{ XK_Cyrillic_tse, 0x0446 }, /* CYRILLIC SMALL LETTER TSE */
+{ XK_Cyrillic_de, 0x0434 }, /* CYRILLIC SMALL LETTER DE */
+{ XK_Cyrillic_ie, 0x0435 }, /* CYRILLIC SMALL LETTER IE */
+{ XK_Cyrillic_ef, 0x0444 }, /* CYRILLIC SMALL LETTER EF */
+{ XK_Cyrillic_ghe, 0x0433 }, /* CYRILLIC SMALL LETTER GHE */
+{ XK_Cyrillic_ha, 0x0445 }, /* CYRILLIC SMALL LETTER HA */
+{ XK_Cyrillic_i, 0x0438 }, /* CYRILLIC SMALL LETTER I */
+{ XK_Cyrillic_shorti, 0x0439 }, /* CYRILLIC SMALL LETTER SHORT I */
+{ XK_Cyrillic_ka, 0x043a }, /* CYRILLIC SMALL LETTER KA */
+{ XK_Cyrillic_el, 0x043b }, /* CYRILLIC SMALL LETTER EL */
+{ XK_Cyrillic_em, 0x043c }, /* CYRILLIC SMALL LETTER EM */
+{ XK_Cyrillic_en, 0x043d }, /* CYRILLIC SMALL LETTER EN */
+{ XK_Cyrillic_o, 0x043e }, /* CYRILLIC SMALL LETTER O */
+{ XK_Cyrillic_pe, 0x043f }, /* CYRILLIC SMALL LETTER PE */
+{ XK_Cyrillic_ya, 0x044f }, /* CYRILLIC SMALL LETTER YA */
+{ XK_Cyrillic_er, 0x0440 }, /* CYRILLIC SMALL LETTER ER */
+{ XK_Cyrillic_es, 0x0441 }, /* CYRILLIC SMALL LETTER ES */
+{ XK_Cyrillic_te, 0x0442 }, /* CYRILLIC SMALL LETTER TE */
+{ XK_Cyrillic_u, 0x0443 }, /* CYRILLIC SMALL LETTER U */
+{ XK_Cyrillic_zhe, 0x0436 }, /* CYRILLIC SMALL LETTER ZHE */
+{ XK_Cyrillic_ve, 0x0432 }, /* CYRILLIC SMALL LETTER VE */
+{ XK_Cyrillic_softsign, 0x044c }, /* CYRILLIC SMALL LETTER SOFT SIGN */
+{ XK_Cyrillic_yeru, 0x044b }, /* CYRILLIC SMALL LETTER YERU */
+{ XK_Cyrillic_ze, 0x0437 }, /* CYRILLIC SMALL LETTER ZE */
+{ XK_Cyrillic_sha, 0x0448 }, /* CYRILLIC SMALL LETTER SHA */
+{ XK_Cyrillic_e, 0x044d }, /* CYRILLIC SMALL LETTER E */
+{ XK_Cyrillic_shcha, 0x0449 }, /* CYRILLIC SMALL LETTER SHCHA */
+{ XK_Cyrillic_che, 0x0447 }, /* CYRILLIC SMALL LETTER CHE */
+{ XK_Cyrillic_hardsign, 0x044a }, /* CYRILLIC SMALL LETTER HARD SIGN */
+{ XK_Cyrillic_YU, 0x042e }, /* CYRILLIC CAPITAL LETTER YU */
+{ XK_Cyrillic_A, 0x0410 }, /* CYRILLIC CAPITAL LETTER A */
+{ XK_Cyrillic_BE, 0x0411 }, /* CYRILLIC CAPITAL LETTER BE */
+{ XK_Cyrillic_TSE, 0x0426 }, /* CYRILLIC CAPITAL LETTER TSE */
+{ XK_Cyrillic_DE, 0x0414 }, /* CYRILLIC CAPITAL LETTER DE */
+{ XK_Cyrillic_IE, 0x0415 }, /* CYRILLIC CAPITAL LETTER IE */
+{ XK_Cyrillic_EF, 0x0424 }, /* CYRILLIC CAPITAL LETTER EF */
+{ XK_Cyrillic_GHE, 0x0413 }, /* CYRILLIC CAPITAL LETTER GHE */
+{ XK_Cyrillic_HA, 0x0425 }, /* CYRILLIC CAPITAL LETTER HA */
+{ XK_Cyrillic_I, 0x0418 }, /* CYRILLIC CAPITAL LETTER I */
+{ XK_Cyrillic_SHORTI, 0x0419 }, /* CYRILLIC CAPITAL LETTER SHORT I */
+{ XK_Cyrillic_KA, 0x041a }, /* CYRILLIC CAPITAL LETTER KA */
+{ XK_Cyrillic_EL, 0x041b }, /* CYRILLIC CAPITAL LETTER EL */
+{ XK_Cyrillic_EM, 0x041c }, /* CYRILLIC CAPITAL LETTER EM */
+{ XK_Cyrillic_EN, 0x041d }, /* CYRILLIC CAPITAL LETTER EN */
+{ XK_Cyrillic_O, 0x041e }, /* CYRILLIC CAPITAL LETTER O */
+{ XK_Cyrillic_PE, 0x041f }, /* CYRILLIC CAPITAL LETTER PE */
+{ XK_Cyrillic_YA, 0x042f }, /* CYRILLIC CAPITAL LETTER YA */
+{ XK_Cyrillic_ER, 0x0420 }, /* CYRILLIC CAPITAL LETTER ER */
+{ XK_Cyrillic_ES, 0x0421 }, /* CYRILLIC CAPITAL LETTER ES */
+{ XK_Cyrillic_TE, 0x0422 }, /* CYRILLIC CAPITAL LETTER TE */
+{ XK_Cyrillic_U, 0x0423 }, /* CYRILLIC CAPITAL LETTER U */
+{ XK_Cyrillic_ZHE, 0x0416 }, /* CYRILLIC CAPITAL LETTER ZHE */
+{ XK_Cyrillic_VE, 0x0412 }, /* CYRILLIC CAPITAL LETTER VE */
+{ XK_Cyrillic_SOFTSIGN, 0x042c }, /* CYRILLIC CAPITAL LETTER SOFT SIGN */
+{ XK_Cyrillic_YERU, 0x042b }, /* CYRILLIC CAPITAL LETTER YERU */
+{ XK_Cyrillic_ZE, 0x0417 }, /* CYRILLIC CAPITAL LETTER ZE */
+{ XK_Cyrillic_SHA, 0x0428 }, /* CYRILLIC CAPITAL LETTER SHA */
+{ XK_Cyrillic_E, 0x042d }, /* CYRILLIC CAPITAL LETTER E */
+{ XK_Cyrillic_SHCHA, 0x0429 }, /* CYRILLIC CAPITAL LETTER SHCHA */
+{ XK_Cyrillic_CHE, 0x0427 }, /* CYRILLIC CAPITAL LETTER CHE */
+{ XK_Cyrillic_HARDSIGN, 0x042a }, /* CYRILLIC CAPITAL LETTER HARD SIGN */
+#endif // defined(XK_Serbian_dje)
+#if defined(XK_Greek_ALPHAaccent)
+{ XK_Greek_ALPHAaccent, 0x0386 }, /* GREEK CAPITAL LETTER ALPHA WITH TONOS */
+{ XK_Greek_EPSILONaccent, 0x0388 }, /* GREEK CAPITAL LETTER EPSILON WITH TONOS */
+{ XK_Greek_ETAaccent, 0x0389 }, /* GREEK CAPITAL LETTER ETA WITH TONOS */
+{ XK_Greek_IOTAaccent, 0x038a }, /* GREEK CAPITAL LETTER IOTA WITH TONOS */
+{ XK_Greek_IOTAdiaeresis, 0x03aa }, /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
+{ XK_Greek_OMICRONaccent, 0x038c }, /* GREEK CAPITAL LETTER OMICRON WITH TONOS */
+{ XK_Greek_UPSILONaccent, 0x038e }, /* GREEK CAPITAL LETTER UPSILON WITH TONOS */
+{ XK_Greek_UPSILONdieresis, 0x03ab }, /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
+{ XK_Greek_OMEGAaccent, 0x038f }, /* GREEK CAPITAL LETTER OMEGA WITH TONOS */
+{ XK_Greek_accentdieresis, 0x0385 }, /* GREEK DIALYTIKA TONOS */
+{ XK_Greek_horizbar, 0x2015 }, /* HORIZONTAL BAR */
+{ XK_Greek_alphaaccent, 0x03ac }, /* GREEK SMALL LETTER ALPHA WITH TONOS */
+{ XK_Greek_epsilonaccent, 0x03ad }, /* GREEK SMALL LETTER EPSILON WITH TONOS */
+{ XK_Greek_etaaccent, 0x03ae }, /* GREEK SMALL LETTER ETA WITH TONOS */
+{ XK_Greek_iotaaccent, 0x03af }, /* GREEK SMALL LETTER IOTA WITH TONOS */
+{ XK_Greek_iotadieresis, 0x03ca }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA */
+{ XK_Greek_iotaaccentdieresis, 0x0390 }, /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
+{ XK_Greek_omicronaccent, 0x03cc }, /* GREEK SMALL LETTER OMICRON WITH TONOS */
+{ XK_Greek_upsilonaccent, 0x03cd }, /* GREEK SMALL LETTER UPSILON WITH TONOS */
+{ XK_Greek_upsilondieresis, 0x03cb }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA */
+{ XK_Greek_upsilonaccentdieresis, 0x03b0 }, /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
+{ XK_Greek_omegaaccent, 0x03ce }, /* GREEK SMALL LETTER OMEGA WITH TONOS */
+{ XK_Greek_ALPHA, 0x0391 }, /* GREEK CAPITAL LETTER ALPHA */
+{ XK_Greek_BETA, 0x0392 }, /* GREEK CAPITAL LETTER BETA */
+{ XK_Greek_GAMMA, 0x0393 }, /* GREEK CAPITAL LETTER GAMMA */
+{ XK_Greek_DELTA, 0x0394 }, /* GREEK CAPITAL LETTER DELTA */
+{ XK_Greek_EPSILON, 0x0395 }, /* GREEK CAPITAL LETTER EPSILON */
+{ XK_Greek_ZETA, 0x0396 }, /* GREEK CAPITAL LETTER ZETA */
+{ XK_Greek_ETA, 0x0397 }, /* GREEK CAPITAL LETTER ETA */
+{ XK_Greek_THETA, 0x0398 }, /* GREEK CAPITAL LETTER THETA */
+{ XK_Greek_IOTA, 0x0399 }, /* GREEK CAPITAL LETTER IOTA */
+{ XK_Greek_KAPPA, 0x039a }, /* GREEK CAPITAL LETTER KAPPA */
+{ XK_Greek_LAMBDA, 0x039b }, /* GREEK CAPITAL LETTER LAMDA */
+{ XK_Greek_MU, 0x039c }, /* GREEK CAPITAL LETTER MU */
+{ XK_Greek_NU, 0x039d }, /* GREEK CAPITAL LETTER NU */
+{ XK_Greek_XI, 0x039e }, /* GREEK CAPITAL LETTER XI */
+{ XK_Greek_OMICRON, 0x039f }, /* GREEK CAPITAL LETTER OMICRON */
+{ XK_Greek_PI, 0x03a0 }, /* GREEK CAPITAL LETTER PI */
+{ XK_Greek_RHO, 0x03a1 }, /* GREEK CAPITAL LETTER RHO */
+{ XK_Greek_SIGMA, 0x03a3 }, /* GREEK CAPITAL LETTER SIGMA */
+{ XK_Greek_TAU, 0x03a4 }, /* GREEK CAPITAL LETTER TAU */
+{ XK_Greek_UPSILON, 0x03a5 }, /* GREEK CAPITAL LETTER UPSILON */
+{ XK_Greek_PHI, 0x03a6 }, /* GREEK CAPITAL LETTER PHI */
+{ XK_Greek_CHI, 0x03a7 }, /* GREEK CAPITAL LETTER CHI */
+{ XK_Greek_PSI, 0x03a8 }, /* GREEK CAPITAL LETTER PSI */
+{ XK_Greek_OMEGA, 0x03a9 }, /* GREEK CAPITAL LETTER OMEGA */
+{ XK_Greek_alpha, 0x03b1 }, /* GREEK SMALL LETTER ALPHA */
+{ XK_Greek_beta, 0x03b2 }, /* GREEK SMALL LETTER BETA */
+{ XK_Greek_gamma, 0x03b3 }, /* GREEK SMALL LETTER GAMMA */
+{ XK_Greek_delta, 0x03b4 }, /* GREEK SMALL LETTER DELTA */
+{ XK_Greek_epsilon, 0x03b5 }, /* GREEK SMALL LETTER EPSILON */
+{ XK_Greek_zeta, 0x03b6 }, /* GREEK SMALL LETTER ZETA */
+{ XK_Greek_eta, 0x03b7 }, /* GREEK SMALL LETTER ETA */
+{ XK_Greek_theta, 0x03b8 }, /* GREEK SMALL LETTER THETA */
+{ XK_Greek_iota, 0x03b9 }, /* GREEK SMALL LETTER IOTA */
+{ XK_Greek_kappa, 0x03ba }, /* GREEK SMALL LETTER KAPPA */
+{ XK_Greek_lambda, 0x03bb }, /* GREEK SMALL LETTER LAMDA */
+{ XK_Greek_mu, 0x03bc }, /* GREEK SMALL LETTER MU */
+{ XK_Greek_nu, 0x03bd }, /* GREEK SMALL LETTER NU */
+{ XK_Greek_xi, 0x03be }, /* GREEK SMALL LETTER XI */
+{ XK_Greek_omicron, 0x03bf }, /* GREEK SMALL LETTER OMICRON */
+{ XK_Greek_pi, 0x03c0 }, /* GREEK SMALL LETTER PI */
+{ XK_Greek_rho, 0x03c1 }, /* GREEK SMALL LETTER RHO */
+{ XK_Greek_sigma, 0x03c3 }, /* GREEK SMALL LETTER SIGMA */
+{ XK_Greek_finalsmallsigma, 0x03c2 }, /* GREEK SMALL LETTER FINAL SIGMA */
+{ XK_Greek_tau, 0x03c4 }, /* GREEK SMALL LETTER TAU */
+{ XK_Greek_upsilon, 0x03c5 }, /* GREEK SMALL LETTER UPSILON */
+{ XK_Greek_phi, 0x03c6 }, /* GREEK SMALL LETTER PHI */
+{ XK_Greek_chi, 0x03c7 }, /* GREEK SMALL LETTER CHI */
+{ XK_Greek_psi, 0x03c8 }, /* GREEK SMALL LETTER PSI */
+{ XK_Greek_omega, 0x03c9 }, /* GREEK SMALL LETTER OMEGA */
+#endif // defined(XK_Greek_ALPHAaccent)
+{ XK_leftradical, 0x23b7 }, /* ??? */
+{ XK_topleftradical, 0x250c }, /* BOX DRAWINGS LIGHT DOWN AND RIGHT */
+{ XK_horizconnector, 0x2500 }, /* BOX DRAWINGS LIGHT HORIZONTAL */
+{ XK_topintegral, 0x2320 }, /* TOP HALF INTEGRAL */
+{ XK_botintegral, 0x2321 }, /* BOTTOM HALF INTEGRAL */
+{ XK_vertconnector, 0x2502 }, /* BOX DRAWINGS LIGHT VERTICAL */
+{ XK_topleftsqbracket, 0x23a1 }, /* ??? */
+{ XK_botleftsqbracket, 0x23a3 }, /* ??? */
+{ XK_toprightsqbracket, 0x23a4 }, /* ??? */
+{ XK_botrightsqbracket, 0x23a6 }, /* ??? */
+{ XK_topleftparens, 0x239b }, /* ??? */
+{ XK_botleftparens, 0x239d }, /* ??? */
+{ XK_toprightparens, 0x239e }, /* ??? */
+{ XK_botrightparens, 0x23a0 }, /* ??? */
+{ XK_leftmiddlecurlybrace, 0x23a8 }, /* ??? */
+{ XK_rightmiddlecurlybrace, 0x23ac }, /* ??? */
+{ XK_lessthanequal, 0x2264 }, /* LESS-THAN OR EQUAL TO */
+{ XK_notequal, 0x2260 }, /* NOT EQUAL TO */
+{ XK_greaterthanequal, 0x2265 }, /* GREATER-THAN OR EQUAL TO */
+{ XK_integral, 0x222b }, /* INTEGRAL */
+{ XK_therefore, 0x2234 }, /* THEREFORE */
+{ XK_variation, 0x221d }, /* PROPORTIONAL TO */
+{ XK_infinity, 0x221e }, /* INFINITY */
+{ XK_nabla, 0x2207 }, /* NABLA */
+{ XK_approximate, 0x223c }, /* TILDE OPERATOR */
+{ XK_similarequal, 0x2243 }, /* ASYMPTOTICALLY EQUAL TO */
+{ XK_ifonlyif, 0x21d4 }, /* LEFT RIGHT DOUBLE ARROW */
+{ XK_implies, 0x21d2 }, /* RIGHTWARDS DOUBLE ARROW */
+{ XK_identical, 0x2261 }, /* IDENTICAL TO */
+{ XK_radical, 0x221a }, /* SQUARE ROOT */
+{ XK_includedin, 0x2282 }, /* SUBSET OF */
+{ XK_includes, 0x2283 }, /* SUPERSET OF */
+{ XK_intersection, 0x2229 }, /* INTERSECTION */
+{ XK_union, 0x222a }, /* UNION */
+{ XK_logicaland, 0x2227 }, /* LOGICAL AND */
+{ XK_logicalor, 0x2228 }, /* LOGICAL OR */
+{ XK_partialderivative, 0x2202 }, /* PARTIAL DIFFERENTIAL */
+{ XK_function, 0x0192 }, /* LATIN SMALL LETTER F WITH HOOK */
+{ XK_leftarrow, 0x2190 }, /* LEFTWARDS ARROW */
+{ XK_uparrow, 0x2191 }, /* UPWARDS ARROW */
+{ XK_rightarrow, 0x2192 }, /* RIGHTWARDS ARROW */
+{ XK_downarrow, 0x2193 }, /* DOWNWARDS ARROW */
+/*{ XK_blank, ??? }, */
+{ XK_soliddiamond, 0x25c6 }, /* BLACK DIAMOND */
+{ XK_checkerboard, 0x2592 }, /* MEDIUM SHADE */
+{ XK_ht, 0x2409 }, /* SYMBOL FOR HORIZONTAL TABULATION */
+{ XK_ff, 0x240c }, /* SYMBOL FOR FORM FEED */
+{ XK_cr, 0x240d }, /* SYMBOL FOR CARRIAGE RETURN */
+{ XK_lf, 0x240a }, /* SYMBOL FOR LINE FEED */
+{ XK_nl, 0x2424 }, /* SYMBOL FOR NEWLINE */
+{ XK_vt, 0x240b }, /* SYMBOL FOR VERTICAL TABULATION */
+{ XK_lowrightcorner, 0x2518 }, /* BOX DRAWINGS LIGHT UP AND LEFT */
+{ XK_uprightcorner, 0x2510 }, /* BOX DRAWINGS LIGHT DOWN AND LEFT */
+{ XK_upleftcorner, 0x250c }, /* BOX DRAWINGS LIGHT DOWN AND RIGHT */
+{ XK_lowleftcorner, 0x2514 }, /* BOX DRAWINGS LIGHT UP AND RIGHT */
+{ XK_crossinglines, 0x253c }, /* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
+{ XK_horizlinescan1, 0x23ba }, /* HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */
+{ XK_horizlinescan3, 0x23bb }, /* HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */
+{ XK_horizlinescan5, 0x2500 }, /* BOX DRAWINGS LIGHT HORIZONTAL */
+{ XK_horizlinescan7, 0x23bc }, /* HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */
+{ XK_horizlinescan9, 0x23bd }, /* HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */
+{ XK_leftt, 0x251c }, /* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
+{ XK_rightt, 0x2524 }, /* BOX DRAWINGS LIGHT VERTICAL AND LEFT */
+{ XK_bott, 0x2534 }, /* BOX DRAWINGS LIGHT UP AND HORIZONTAL */
+{ XK_topt, 0x252c }, /* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
+{ XK_vertbar, 0x2502 }, /* BOX DRAWINGS LIGHT VERTICAL */
+{ XK_emspace, 0x2003 }, /* EM SPACE */
+{ XK_enspace, 0x2002 }, /* EN SPACE */
+{ XK_em3space, 0x2004 }, /* THREE-PER-EM SPACE */
+{ XK_em4space, 0x2005 }, /* FOUR-PER-EM SPACE */
+{ XK_digitspace, 0x2007 }, /* FIGURE SPACE */
+{ XK_punctspace, 0x2008 }, /* PUNCTUATION SPACE */
+{ XK_thinspace, 0x2009 }, /* THIN SPACE */
+{ XK_hairspace, 0x200a }, /* HAIR SPACE */
+{ XK_emdash, 0x2014 }, /* EM DASH */
+{ XK_endash, 0x2013 }, /* EN DASH */
+/*{ XK_signifblank, ??? }, */
+{ XK_ellipsis, 0x2026 }, /* HORIZONTAL ELLIPSIS */
+{ XK_doubbaselinedot, 0x2025 }, /* TWO DOT LEADER */
+{ XK_onethird, 0x2153 }, /* VULGAR FRACTION ONE THIRD */
+{ XK_twothirds, 0x2154 }, /* VULGAR FRACTION TWO THIRDS */
+{ XK_onefifth, 0x2155 }, /* VULGAR FRACTION ONE FIFTH */
+{ XK_twofifths, 0x2156 }, /* VULGAR FRACTION TWO FIFTHS */
+{ XK_threefifths, 0x2157 }, /* VULGAR FRACTION THREE FIFTHS */
+{ XK_fourfifths, 0x2158 }, /* VULGAR FRACTION FOUR FIFTHS */
+{ XK_onesixth, 0x2159 }, /* VULGAR FRACTION ONE SIXTH */
+{ XK_fivesixths, 0x215a }, /* VULGAR FRACTION FIVE SIXTHS */
+{ XK_careof, 0x2105 }, /* CARE OF */
+{ XK_figdash, 0x2012 }, /* FIGURE DASH */
+{ XK_leftanglebracket, 0x2329 }, /* LEFT-POINTING ANGLE BRACKET */
+/*{ XK_decimalpoint, ??? }, */
+{ XK_rightanglebracket, 0x232a }, /* RIGHT-POINTING ANGLE BRACKET */
+/*{ XK_marker, ??? }, */
+{ XK_oneeighth, 0x215b }, /* VULGAR FRACTION ONE EIGHTH */
+{ XK_threeeighths, 0x215c }, /* VULGAR FRACTION THREE EIGHTHS */
+{ XK_fiveeighths, 0x215d }, /* VULGAR FRACTION FIVE EIGHTHS */
+{ XK_seveneighths, 0x215e }, /* VULGAR FRACTION SEVEN EIGHTHS */
+{ XK_trademark, 0x2122 }, /* TRADE MARK SIGN */
+{ XK_signaturemark, 0x2613 }, /* SALTIRE */
+/*{ XK_trademarkincircle, ??? }, */
+{ XK_leftopentriangle, 0x25c1 }, /* WHITE LEFT-POINTING TRIANGLE */
+{ XK_rightopentriangle, 0x25b7 }, /* WHITE RIGHT-POINTING TRIANGLE */
+{ XK_emopencircle, 0x25cb }, /* WHITE CIRCLE */
+{ XK_emopenrectangle, 0x25af }, /* WHITE VERTICAL RECTANGLE */
+{ XK_leftsinglequotemark, 0x2018 }, /* LEFT SINGLE QUOTATION MARK */
+{ XK_rightsinglequotemark, 0x2019 }, /* RIGHT SINGLE QUOTATION MARK */
+{ XK_leftdoublequotemark, 0x201c }, /* LEFT DOUBLE QUOTATION MARK */
+{ XK_rightdoublequotemark, 0x201d }, /* RIGHT DOUBLE QUOTATION MARK */
+{ XK_prescription, 0x211e }, /* PRESCRIPTION TAKE */
+{ XK_minutes, 0x2032 }, /* PRIME */
+{ XK_seconds, 0x2033 }, /* DOUBLE PRIME */
+{ XK_latincross, 0x271d }, /* LATIN CROSS */
+/*{ XK_hexagram, ??? }, */
+{ XK_filledrectbullet, 0x25ac }, /* BLACK RECTANGLE */
+{ XK_filledlefttribullet, 0x25c0 }, /* BLACK LEFT-POINTING TRIANGLE */
+{ XK_filledrighttribullet, 0x25b6 }, /* BLACK RIGHT-POINTING TRIANGLE */
+{ XK_emfilledcircle, 0x25cf }, /* BLACK CIRCLE */
+{ XK_emfilledrect, 0x25ae }, /* BLACK VERTICAL RECTANGLE */
+{ XK_enopencircbullet, 0x25e6 }, /* WHITE BULLET */
+{ XK_enopensquarebullet, 0x25ab }, /* WHITE SMALL SQUARE */
+{ XK_openrectbullet, 0x25ad }, /* WHITE RECTANGLE */
+{ XK_opentribulletup, 0x25b3 }, /* WHITE UP-POINTING TRIANGLE */
+{ XK_opentribulletdown, 0x25bd }, /* WHITE DOWN-POINTING TRIANGLE */
+{ XK_openstar, 0x2606 }, /* WHITE STAR */
+{ XK_enfilledcircbullet, 0x2022 }, /* BULLET */
+{ XK_enfilledsqbullet, 0x25aa }, /* BLACK SMALL SQUARE */
+{ XK_filledtribulletup, 0x25b2 }, /* BLACK UP-POINTING TRIANGLE */
+{ XK_filledtribulletdown, 0x25bc }, /* BLACK DOWN-POINTING TRIANGLE */
+{ XK_leftpointer, 0x261c }, /* WHITE LEFT POINTING INDEX */
+{ XK_rightpointer, 0x261e }, /* WHITE RIGHT POINTING INDEX */
+{ XK_club, 0x2663 }, /* BLACK CLUB SUIT */
+{ XK_diamond, 0x2666 }, /* BLACK DIAMOND SUIT */
+{ XK_heart, 0x2665 }, /* BLACK HEART SUIT */
+{ XK_maltesecross, 0x2720 }, /* MALTESE CROSS */
+{ XK_dagger, 0x2020 }, /* DAGGER */
+{ XK_doubledagger, 0x2021 }, /* DOUBLE DAGGER */
+{ XK_checkmark, 0x2713 }, /* CHECK MARK */
+{ XK_ballotcross, 0x2717 }, /* BALLOT X */
+{ XK_musicalsharp, 0x266f }, /* MUSIC SHARP SIGN */
+{ XK_musicalflat, 0x266d }, /* MUSIC FLAT SIGN */
+{ XK_malesymbol, 0x2642 }, /* MALE SIGN */
+{ XK_femalesymbol, 0x2640 }, /* FEMALE SIGN */
+{ XK_telephone, 0x260e }, /* BLACK TELEPHONE */
+{ XK_telephonerecorder, 0x2315 }, /* TELEPHONE RECORDER */
+{ XK_phonographcopyright, 0x2117 }, /* SOUND RECORDING COPYRIGHT */
+{ XK_caret, 0x2038 }, /* CARET */
+{ XK_singlelowquotemark, 0x201a }, /* SINGLE LOW-9 QUOTATION MARK */
+{ XK_doublelowquotemark, 0x201e }, /* DOUBLE LOW-9 QUOTATION MARK */
+/*{ XK_cursor, ??? }, */
+{ XK_leftcaret, 0x003c }, /* LESS-THAN SIGN */
+{ XK_rightcaret, 0x003e }, /* GREATER-THAN SIGN */
+{ XK_downcaret, 0x2228 }, /* LOGICAL OR */
+{ XK_upcaret, 0x2227 }, /* LOGICAL AND */
+{ XK_overbar, 0x00af }, /* MACRON */
+{ XK_downtack, 0x22a5 }, /* UP TACK */
+{ XK_upshoe, 0x2229 }, /* INTERSECTION */
+{ XK_downstile, 0x230a }, /* LEFT FLOOR */
+{ XK_underbar, 0x005f }, /* LOW LINE */
+{ XK_jot, 0x2218 }, /* RING OPERATOR */
+{ XK_quad, 0x2395 }, /* APL FUNCTIONAL SYMBOL QUAD */
+{ XK_uptack, 0x22a4 }, /* DOWN TACK */
+{ XK_circle, 0x25cb }, /* WHITE CIRCLE */
+{ XK_upstile, 0x2308 }, /* LEFT CEILING */
+{ XK_downshoe, 0x222a }, /* UNION */
+{ XK_rightshoe, 0x2283 }, /* SUPERSET OF */
+{ XK_leftshoe, 0x2282 }, /* SUBSET OF */
+{ XK_lefttack, 0x22a2 }, /* RIGHT TACK */
+{ XK_righttack, 0x22a3 }, /* LEFT TACK */
+#if defined(XK_hebrew_doublelowline)
+{ XK_hebrew_doublelowline, 0x2017 }, /* DOUBLE LOW LINE */
+{ XK_hebrew_aleph, 0x05d0 }, /* HEBREW LETTER ALEF */
+{ XK_hebrew_bet, 0x05d1 }, /* HEBREW LETTER BET */
+{ XK_hebrew_gimel, 0x05d2 }, /* HEBREW LETTER GIMEL */
+{ XK_hebrew_dalet, 0x05d3 }, /* HEBREW LETTER DALET */
+{ XK_hebrew_he, 0x05d4 }, /* HEBREW LETTER HE */
+{ XK_hebrew_waw, 0x05d5 }, /* HEBREW LETTER VAV */
+{ XK_hebrew_zain, 0x05d6 }, /* HEBREW LETTER ZAYIN */
+{ XK_hebrew_chet, 0x05d7 }, /* HEBREW LETTER HET */
+{ XK_hebrew_tet, 0x05d8 }, /* HEBREW LETTER TET */
+{ XK_hebrew_yod, 0x05d9 }, /* HEBREW LETTER YOD */
+{ XK_hebrew_finalkaph, 0x05da }, /* HEBREW LETTER FINAL KAF */
+{ XK_hebrew_kaph, 0x05db }, /* HEBREW LETTER KAF */
+{ XK_hebrew_lamed, 0x05dc }, /* HEBREW LETTER LAMED */
+{ XK_hebrew_finalmem, 0x05dd }, /* HEBREW LETTER FINAL MEM */
+{ XK_hebrew_mem, 0x05de }, /* HEBREW LETTER MEM */
+{ XK_hebrew_finalnun, 0x05df }, /* HEBREW LETTER FINAL NUN */
+{ XK_hebrew_nun, 0x05e0 }, /* HEBREW LETTER NUN */
+{ XK_hebrew_samech, 0x05e1 }, /* HEBREW LETTER SAMEKH */
+{ XK_hebrew_ayin, 0x05e2 }, /* HEBREW LETTER AYIN */
+{ XK_hebrew_finalpe, 0x05e3 }, /* HEBREW LETTER FINAL PE */
+{ XK_hebrew_pe, 0x05e4 }, /* HEBREW LETTER PE */
+{ XK_hebrew_finalzade, 0x05e5 }, /* HEBREW LETTER FINAL TSADI */
+{ XK_hebrew_zade, 0x05e6 }, /* HEBREW LETTER TSADI */
+{ XK_hebrew_qoph, 0x05e7 }, /* HEBREW LETTER QOF */
+{ XK_hebrew_resh, 0x05e8 }, /* HEBREW LETTER RESH */
+{ XK_hebrew_shin, 0x05e9 }, /* HEBREW LETTER SHIN */
+{ XK_hebrew_taw, 0x05ea }, /* HEBREW LETTER TAV */
+#endif // defined(XK_hebrew_doublelowline)
+#if defined(XK_Thai_kokai)
+{ XK_Thai_kokai, 0x0e01 }, /* THAI CHARACTER KO KAI */
+{ XK_Thai_khokhai, 0x0e02 }, /* THAI CHARACTER KHO KHAI */
+{ XK_Thai_khokhuat, 0x0e03 }, /* THAI CHARACTER KHO KHUAT */
+{ XK_Thai_khokhwai, 0x0e04 }, /* THAI CHARACTER KHO KHWAI */
+{ XK_Thai_khokhon, 0x0e05 }, /* THAI CHARACTER KHO KHON */
+{ XK_Thai_khorakhang, 0x0e06 }, /* THAI CHARACTER KHO RAKHANG */
+{ XK_Thai_ngongu, 0x0e07 }, /* THAI CHARACTER NGO NGU */
+{ XK_Thai_chochan, 0x0e08 }, /* THAI CHARACTER CHO CHAN */
+{ XK_Thai_choching, 0x0e09 }, /* THAI CHARACTER CHO CHING */
+{ XK_Thai_chochang, 0x0e0a }, /* THAI CHARACTER CHO CHANG */
+{ XK_Thai_soso, 0x0e0b }, /* THAI CHARACTER SO SO */
+{ XK_Thai_chochoe, 0x0e0c }, /* THAI CHARACTER CHO CHOE */
+{ XK_Thai_yoying, 0x0e0d }, /* THAI CHARACTER YO YING */
+{ XK_Thai_dochada, 0x0e0e }, /* THAI CHARACTER DO CHADA */
+{ XK_Thai_topatak, 0x0e0f }, /* THAI CHARACTER TO PATAK */
+{ XK_Thai_thothan, 0x0e10 }, /* THAI CHARACTER THO THAN */
+{ XK_Thai_thonangmontho, 0x0e11 }, /* THAI CHARACTER THO NANGMONTHO */
+{ XK_Thai_thophuthao, 0x0e12 }, /* THAI CHARACTER THO PHUTHAO */
+{ XK_Thai_nonen, 0x0e13 }, /* THAI CHARACTER NO NEN */
+{ XK_Thai_dodek, 0x0e14 }, /* THAI CHARACTER DO DEK */
+{ XK_Thai_totao, 0x0e15 }, /* THAI CHARACTER TO TAO */
+{ XK_Thai_thothung, 0x0e16 }, /* THAI CHARACTER THO THUNG */
+{ XK_Thai_thothahan, 0x0e17 }, /* THAI CHARACTER THO THAHAN */
+{ XK_Thai_thothong, 0x0e18 }, /* THAI CHARACTER THO THONG */
+{ XK_Thai_nonu, 0x0e19 }, /* THAI CHARACTER NO NU */
+{ XK_Thai_bobaimai, 0x0e1a }, /* THAI CHARACTER BO BAIMAI */
+{ XK_Thai_popla, 0x0e1b }, /* THAI CHARACTER PO PLA */
+{ XK_Thai_phophung, 0x0e1c }, /* THAI CHARACTER PHO PHUNG */
+{ XK_Thai_fofa, 0x0e1d }, /* THAI CHARACTER FO FA */
+{ XK_Thai_phophan, 0x0e1e }, /* THAI CHARACTER PHO PHAN */
+{ XK_Thai_fofan, 0x0e1f }, /* THAI CHARACTER FO FAN */
+{ XK_Thai_phosamphao, 0x0e20 }, /* THAI CHARACTER PHO SAMPHAO */
+{ XK_Thai_moma, 0x0e21 }, /* THAI CHARACTER MO MA */
+{ XK_Thai_yoyak, 0x0e22 }, /* THAI CHARACTER YO YAK */
+{ XK_Thai_rorua, 0x0e23 }, /* THAI CHARACTER RO RUA */
+{ XK_Thai_ru, 0x0e24 }, /* THAI CHARACTER RU */
+{ XK_Thai_loling, 0x0e25 }, /* THAI CHARACTER LO LING */
+{ XK_Thai_lu, 0x0e26 }, /* THAI CHARACTER LU */
+{ XK_Thai_wowaen, 0x0e27 }, /* THAI CHARACTER WO WAEN */
+{ XK_Thai_sosala, 0x0e28 }, /* THAI CHARACTER SO SALA */
+{ XK_Thai_sorusi, 0x0e29 }, /* THAI CHARACTER SO RUSI */
+{ XK_Thai_sosua, 0x0e2a }, /* THAI CHARACTER SO SUA */
+{ XK_Thai_hohip, 0x0e2b }, /* THAI CHARACTER HO HIP */
+{ XK_Thai_lochula, 0x0e2c }, /* THAI CHARACTER LO CHULA */
+{ XK_Thai_oang, 0x0e2d }, /* THAI CHARACTER O ANG */
+{ XK_Thai_honokhuk, 0x0e2e }, /* THAI CHARACTER HO NOKHUK */
+{ XK_Thai_paiyannoi, 0x0e2f }, /* THAI CHARACTER PAIYANNOI */
+{ XK_Thai_saraa, 0x0e30 }, /* THAI CHARACTER SARA A */
+{ XK_Thai_maihanakat, 0x0e31 }, /* THAI CHARACTER MAI HAN-AKAT */
+{ XK_Thai_saraaa, 0x0e32 }, /* THAI CHARACTER SARA AA */
+{ XK_Thai_saraam, 0x0e33 }, /* THAI CHARACTER SARA AM */
+{ XK_Thai_sarai, 0x0e34 }, /* THAI CHARACTER SARA I */
+{ XK_Thai_saraii, 0x0e35 }, /* THAI CHARACTER SARA II */
+{ XK_Thai_saraue, 0x0e36 }, /* THAI CHARACTER SARA UE */
+{ XK_Thai_sarauee, 0x0e37 }, /* THAI CHARACTER SARA UEE */
+{ XK_Thai_sarau, 0x0e38 }, /* THAI CHARACTER SARA U */
+{ XK_Thai_sarauu, 0x0e39 }, /* THAI CHARACTER SARA UU */
+{ XK_Thai_phinthu, 0x0e3a }, /* THAI CHARACTER PHINTHU */
+/*{ XK_Thai_maihanakat_maitho, ??? }, */
+{ XK_Thai_baht, 0x0e3f }, /* THAI CURRENCY SYMBOL BAHT */
+{ XK_Thai_sarae, 0x0e40 }, /* THAI CHARACTER SARA E */
+{ XK_Thai_saraae, 0x0e41 }, /* THAI CHARACTER SARA AE */
+{ XK_Thai_sarao, 0x0e42 }, /* THAI CHARACTER SARA O */
+{ XK_Thai_saraaimaimuan, 0x0e43 }, /* THAI CHARACTER SARA AI MAIMUAN */
+{ XK_Thai_saraaimaimalai, 0x0e44 }, /* THAI CHARACTER SARA AI MAIMALAI */
+{ XK_Thai_lakkhangyao, 0x0e45 }, /* THAI CHARACTER LAKKHANGYAO */
+{ XK_Thai_maiyamok, 0x0e46 }, /* THAI CHARACTER MAIYAMOK */
+{ XK_Thai_maitaikhu, 0x0e47 }, /* THAI CHARACTER MAITAIKHU */
+{ XK_Thai_maiek, 0x0e48 }, /* THAI CHARACTER MAI EK */
+{ XK_Thai_maitho, 0x0e49 }, /* THAI CHARACTER MAI THO */
+{ XK_Thai_maitri, 0x0e4a }, /* THAI CHARACTER MAI TRI */
+{ XK_Thai_maichattawa, 0x0e4b }, /* THAI CHARACTER MAI CHATTAWA */
+{ XK_Thai_thanthakhat, 0x0e4c }, /* THAI CHARACTER THANTHAKHAT */
+{ XK_Thai_nikhahit, 0x0e4d }, /* THAI CHARACTER NIKHAHIT */
+{ XK_Thai_leksun, 0x0e50 }, /* THAI DIGIT ZERO */
+{ XK_Thai_leknung, 0x0e51 }, /* THAI DIGIT ONE */
+{ XK_Thai_leksong, 0x0e52 }, /* THAI DIGIT TWO */
+{ XK_Thai_leksam, 0x0e53 }, /* THAI DIGIT THREE */
+{ XK_Thai_leksi, 0x0e54 }, /* THAI DIGIT FOUR */
+{ XK_Thai_lekha, 0x0e55 }, /* THAI DIGIT FIVE */
+{ XK_Thai_lekhok, 0x0e56 }, /* THAI DIGIT SIX */
+{ XK_Thai_lekchet, 0x0e57 }, /* THAI DIGIT SEVEN */
+{ XK_Thai_lekpaet, 0x0e58 }, /* THAI DIGIT EIGHT */
+{ XK_Thai_lekkao, 0x0e59 }, /* THAI DIGIT NINE */
+#endif // defined(XK_Thai_kokai)
+#if defined(XK_Hangul_Kiyeog)
+{ XK_Hangul_Kiyeog, 0x3131 }, /* HANGUL LETTER KIYEOK */
+{ XK_Hangul_SsangKiyeog, 0x3132 }, /* HANGUL LETTER SSANGKIYEOK */
+{ XK_Hangul_KiyeogSios, 0x3133 }, /* HANGUL LETTER KIYEOK-SIOS */
+{ XK_Hangul_Nieun, 0x3134 }, /* HANGUL LETTER NIEUN */
+{ XK_Hangul_NieunJieuj, 0x3135 }, /* HANGUL LETTER NIEUN-CIEUC */
+{ XK_Hangul_NieunHieuh, 0x3136 }, /* HANGUL LETTER NIEUN-HIEUH */
+{ XK_Hangul_Dikeud, 0x3137 }, /* HANGUL LETTER TIKEUT */
+{ XK_Hangul_SsangDikeud, 0x3138 }, /* HANGUL LETTER SSANGTIKEUT */
+{ XK_Hangul_Rieul, 0x3139 }, /* HANGUL LETTER RIEUL */
+{ XK_Hangul_RieulKiyeog, 0x313a }, /* HANGUL LETTER RIEUL-KIYEOK */
+{ XK_Hangul_RieulMieum, 0x313b }, /* HANGUL LETTER RIEUL-MIEUM */
+{ XK_Hangul_RieulPieub, 0x313c }, /* HANGUL LETTER RIEUL-PIEUP */
+{ XK_Hangul_RieulSios, 0x313d }, /* HANGUL LETTER RIEUL-SIOS */
+{ XK_Hangul_RieulTieut, 0x313e }, /* HANGUL LETTER RIEUL-THIEUTH */
+{ XK_Hangul_RieulPhieuf, 0x313f }, /* HANGUL LETTER RIEUL-PHIEUPH */
+{ XK_Hangul_RieulHieuh, 0x3140 }, /* HANGUL LETTER RIEUL-HIEUH */
+{ XK_Hangul_Mieum, 0x3141 }, /* HANGUL LETTER MIEUM */
+{ XK_Hangul_Pieub, 0x3142 }, /* HANGUL LETTER PIEUP */
+{ XK_Hangul_SsangPieub, 0x3143 }, /* HANGUL LETTER SSANGPIEUP */
+{ XK_Hangul_PieubSios, 0x3144 }, /* HANGUL LETTER PIEUP-SIOS */
+{ XK_Hangul_Sios, 0x3145 }, /* HANGUL LETTER SIOS */
+{ XK_Hangul_SsangSios, 0x3146 }, /* HANGUL LETTER SSANGSIOS */
+{ XK_Hangul_Ieung, 0x3147 }, /* HANGUL LETTER IEUNG */
+{ XK_Hangul_Jieuj, 0x3148 }, /* HANGUL LETTER CIEUC */
+{ XK_Hangul_SsangJieuj, 0x3149 }, /* HANGUL LETTER SSANGCIEUC */
+{ XK_Hangul_Cieuc, 0x314a }, /* HANGUL LETTER CHIEUCH */
+{ XK_Hangul_Khieuq, 0x314b }, /* HANGUL LETTER KHIEUKH */
+{ XK_Hangul_Tieut, 0x314c }, /* HANGUL LETTER THIEUTH */
+{ XK_Hangul_Phieuf, 0x314d }, /* HANGUL LETTER PHIEUPH */
+{ XK_Hangul_Hieuh, 0x314e }, /* HANGUL LETTER HIEUH */
+{ XK_Hangul_A, 0x314f }, /* HANGUL LETTER A */
+{ XK_Hangul_AE, 0x3150 }, /* HANGUL LETTER AE */
+{ XK_Hangul_YA, 0x3151 }, /* HANGUL LETTER YA */
+{ XK_Hangul_YAE, 0x3152 }, /* HANGUL LETTER YAE */
+{ XK_Hangul_EO, 0x3153 }, /* HANGUL LETTER EO */
+{ XK_Hangul_E, 0x3154 }, /* HANGUL LETTER E */
+{ XK_Hangul_YEO, 0x3155 }, /* HANGUL LETTER YEO */
+{ XK_Hangul_YE, 0x3156 }, /* HANGUL LETTER YE */
+{ XK_Hangul_O, 0x3157 }, /* HANGUL LETTER O */
+{ XK_Hangul_WA, 0x3158 }, /* HANGUL LETTER WA */
+{ XK_Hangul_WAE, 0x3159 }, /* HANGUL LETTER WAE */
+{ XK_Hangul_OE, 0x315a }, /* HANGUL LETTER OE */
+{ XK_Hangul_YO, 0x315b }, /* HANGUL LETTER YO */
+{ XK_Hangul_U, 0x315c }, /* HANGUL LETTER U */
+{ XK_Hangul_WEO, 0x315d }, /* HANGUL LETTER WEO */
+{ XK_Hangul_WE, 0x315e }, /* HANGUL LETTER WE */
+{ XK_Hangul_WI, 0x315f }, /* HANGUL LETTER WI */
+{ XK_Hangul_YU, 0x3160 }, /* HANGUL LETTER YU */
+{ XK_Hangul_EU, 0x3161 }, /* HANGUL LETTER EU */
+{ XK_Hangul_YI, 0x3162 }, /* HANGUL LETTER YI */
+{ XK_Hangul_I, 0x3163 }, /* HANGUL LETTER I */
+{ XK_Hangul_J_Kiyeog, 0x11a8 }, /* HANGUL JONGSEONG KIYEOK */
+{ XK_Hangul_J_SsangKiyeog, 0x11a9 }, /* HANGUL JONGSEONG SSANGKIYEOK */
+{ XK_Hangul_J_KiyeogSios, 0x11aa }, /* HANGUL JONGSEONG KIYEOK-SIOS */
+{ XK_Hangul_J_Nieun, 0x11ab }, /* HANGUL JONGSEONG NIEUN */
+{ XK_Hangul_J_NieunJieuj, 0x11ac }, /* HANGUL JONGSEONG NIEUN-CIEUC */
+{ XK_Hangul_J_NieunHieuh, 0x11ad }, /* HANGUL JONGSEONG NIEUN-HIEUH */
+{ XK_Hangul_J_Dikeud, 0x11ae }, /* HANGUL JONGSEONG TIKEUT */
+{ XK_Hangul_J_Rieul, 0x11af }, /* HANGUL JONGSEONG RIEUL */
+{ XK_Hangul_J_RieulKiyeog, 0x11b0 }, /* HANGUL JONGSEONG RIEUL-KIYEOK */
+{ XK_Hangul_J_RieulMieum, 0x11b1 }, /* HANGUL JONGSEONG RIEUL-MIEUM */
+{ XK_Hangul_J_RieulPieub, 0x11b2 }, /* HANGUL JONGSEONG RIEUL-PIEUP */
+{ XK_Hangul_J_RieulSios, 0x11b3 }, /* HANGUL JONGSEONG RIEUL-SIOS */
+{ XK_Hangul_J_RieulTieut, 0x11b4 }, /* HANGUL JONGSEONG RIEUL-THIEUTH */
+{ XK_Hangul_J_RieulPhieuf, 0x11b5 }, /* HANGUL JONGSEONG RIEUL-PHIEUPH */
+{ XK_Hangul_J_RieulHieuh, 0x11b6 }, /* HANGUL JONGSEONG RIEUL-HIEUH */
+{ XK_Hangul_J_Mieum, 0x11b7 }, /* HANGUL JONGSEONG MIEUM */
+{ XK_Hangul_J_Pieub, 0x11b8 }, /* HANGUL JONGSEONG PIEUP */
+{ XK_Hangul_J_PieubSios, 0x11b9 }, /* HANGUL JONGSEONG PIEUP-SIOS */
+{ XK_Hangul_J_Sios, 0x11ba }, /* HANGUL JONGSEONG SIOS */
+{ XK_Hangul_J_SsangSios, 0x11bb }, /* HANGUL JONGSEONG SSANGSIOS */
+{ XK_Hangul_J_Ieung, 0x11bc }, /* HANGUL JONGSEONG IEUNG */
+{ XK_Hangul_J_Jieuj, 0x11bd }, /* HANGUL JONGSEONG CIEUC */
+{ XK_Hangul_J_Cieuc, 0x11be }, /* HANGUL JONGSEONG CHIEUCH */
+{ XK_Hangul_J_Khieuq, 0x11bf }, /* HANGUL JONGSEONG KHIEUKH */
+{ XK_Hangul_J_Tieut, 0x11c0 }, /* HANGUL JONGSEONG THIEUTH */
+{ XK_Hangul_J_Phieuf, 0x11c1 }, /* HANGUL JONGSEONG PHIEUPH */
+{ XK_Hangul_J_Hieuh, 0x11c2 }, /* HANGUL JONGSEONG HIEUH */
+{ XK_Hangul_RieulYeorinHieuh, 0x316d }, /* HANGUL LETTER RIEUL-YEORINHIEUH */
+{ XK_Hangul_SunkyeongeumMieum, 0x3171 }, /* HANGUL LETTER KAPYEOUNMIEUM */
+{ XK_Hangul_SunkyeongeumPieub, 0x3178 }, /* HANGUL LETTER KAPYEOUNPIEUP */
+{ XK_Hangul_PanSios, 0x317f }, /* HANGUL LETTER PANSIOS */
+{ XK_Hangul_KkogjiDalrinIeung, 0x3181 }, /* HANGUL LETTER YESIEUNG */
+{ XK_Hangul_SunkyeongeumPhieuf, 0x3184 }, /* HANGUL LETTER KAPYEOUNPHIEUPH */
+{ XK_Hangul_YeorinHieuh, 0x3186 }, /* HANGUL LETTER YEORINHIEUH */
+{ XK_Hangul_AraeA, 0x318d }, /* HANGUL LETTER ARAEA */
+{ XK_Hangul_AraeAE, 0x318e }, /* HANGUL LETTER ARAEAE */
+{ XK_Hangul_J_PanSios, 0x11eb }, /* HANGUL JONGSEONG PANSIOS */
+{ XK_Hangul_J_KkogjiDalrinIeung, 0x11f0 }, /* HANGUL JONGSEONG YESIEUNG */
+{ XK_Hangul_J_YeorinHieuh, 0x11f9 }, /* HANGUL JONGSEONG YEORINHIEUH */
+{ XK_Korean_Won, 0x20a9 }, /* WON SIGN */
+#endif // defined(XK_Hangul_Kiyeog)
+{ XK_OE, 0x0152 }, /* LATIN CAPITAL LIGATURE OE */
+{ XK_oe, 0x0153 }, /* LATIN SMALL LIGATURE OE */
+{ XK_Ydiaeresis, 0x0178 }, /* LATIN CAPITAL LETTER Y WITH DIAERESIS */
+{ XK_EuroSign, 0x20ac }, /* EURO SIGN */
+
+/* combining dead keys */
+{ XK_dead_abovedot, 0x0307 }, /* COMBINING DOT ABOVE */
+{ XK_dead_abovering, 0x030a }, /* COMBINING RING ABOVE */
+{ XK_dead_acute, 0x0301 }, /* COMBINING ACUTE ACCENT */
+{ XK_dead_breve, 0x0306 }, /* COMBINING BREVE */
+{ XK_dead_caron, 0x030c }, /* COMBINING CARON */
+{ XK_dead_cedilla, 0x0327 }, /* COMBINING CEDILLA */
+{ XK_dead_circumflex, 0x0302 }, /* COMBINING CIRCUMFLEX ACCENT */
+{ XK_dead_diaeresis, 0x0308 }, /* COMBINING DIAERESIS */
+{ XK_dead_doubleacute, 0x030b }, /* COMBINING DOUBLE ACUTE ACCENT */
+{ XK_dead_grave, 0x0300 }, /* COMBINING GRAVE ACCENT */
+{ XK_dead_macron, 0x0304 }, /* COMBINING MACRON */
+{ XK_dead_ogonek, 0x0328 }, /* COMBINING OGONEK */
+{ XK_dead_tilde, 0x0303 } /* COMBINING TILDE */
+};
+/* XXX -- map these too
+XK_Cyrillic_GHE_bar
+XK_Cyrillic_ZHE_descender
+XK_Cyrillic_KA_descender
+XK_Cyrillic_KA_vertstroke
+XK_Cyrillic_EN_descender
+XK_Cyrillic_U_straight
+XK_Cyrillic_U_straight_bar
+XK_Cyrillic_HA_descender
+XK_Cyrillic_CHE_descender
+XK_Cyrillic_CHE_vertstroke
+XK_Cyrillic_SHHA
+XK_Cyrillic_SCHWA
+XK_Cyrillic_I_macron
+XK_Cyrillic_O_bar
+XK_Cyrillic_U_macron
+XK_Cyrillic_ghe_bar
+XK_Cyrillic_zhe_descender
+XK_Cyrillic_ka_descender
+XK_Cyrillic_ka_vertstroke
+XK_Cyrillic_en_descender
+XK_Cyrillic_u_straight
+XK_Cyrillic_u_straight_bar
+XK_Cyrillic_ha_descender
+XK_Cyrillic_che_descender
+XK_Cyrillic_che_vertstroke
+XK_Cyrillic_shha
+XK_Cyrillic_schwa
+XK_Cyrillic_i_macron
+XK_Cyrillic_o_bar
+XK_Cyrillic_u_macron
+
+XK_Armenian_eternity
+XK_Armenian_ligature_ew
+XK_Armenian_full_stop
+XK_Armenian_verjaket
+XK_Armenian_parenright
+XK_Armenian_parenleft
+XK_Armenian_guillemotright
+XK_Armenian_guillemotleft
+XK_Armenian_em_dash
+XK_Armenian_dot
+XK_Armenian_mijaket
+XK_Armenian_but
+XK_Armenian_separation_mark
+XK_Armenian_comma
+XK_Armenian_en_dash
+XK_Armenian_hyphen
+XK_Armenian_yentamna
+XK_Armenian_ellipsis
+XK_Armenian_amanak
+XK_Armenian_exclam
+XK_Armenian_accent
+XK_Armenian_shesht
+XK_Armenian_paruyk
+XK_Armenian_question
+XK_Armenian_AYB
+XK_Armenian_ayb
+XK_Armenian_BEN
+XK_Armenian_ben
+XK_Armenian_GIM
+XK_Armenian_gim
+XK_Armenian_DA
+XK_Armenian_da
+XK_Armenian_YECH
+XK_Armenian_yech
+XK_Armenian_ZA
+XK_Armenian_za
+XK_Armenian_E
+XK_Armenian_e
+XK_Armenian_AT
+XK_Armenian_at
+XK_Armenian_TO
+XK_Armenian_to
+XK_Armenian_ZHE
+XK_Armenian_zhe
+XK_Armenian_INI
+XK_Armenian_ini
+XK_Armenian_LYUN
+XK_Armenian_lyun
+XK_Armenian_KHE
+XK_Armenian_khe
+XK_Armenian_TSA
+XK_Armenian_tsa
+XK_Armenian_KEN
+XK_Armenian_ken
+XK_Armenian_HO
+XK_Armenian_ho
+XK_Armenian_DZA
+XK_Armenian_dza
+XK_Armenian_GHAT
+XK_Armenian_ghat
+XK_Armenian_TCHE
+XK_Armenian_tche
+XK_Armenian_MEN
+XK_Armenian_men
+XK_Armenian_HI
+XK_Armenian_hi
+XK_Armenian_NU
+XK_Armenian_nu
+XK_Armenian_SHA
+XK_Armenian_sha
+XK_Armenian_VO
+XK_Armenian_vo
+XK_Armenian_CHA
+XK_Armenian_cha
+XK_Armenian_PE
+XK_Armenian_pe
+XK_Armenian_JE
+XK_Armenian_je
+XK_Armenian_RA
+XK_Armenian_ra
+XK_Armenian_SE
+XK_Armenian_se
+XK_Armenian_VEV
+XK_Armenian_vev
+XK_Armenian_TYUN
+XK_Armenian_tyun
+XK_Armenian_RE
+XK_Armenian_re
+XK_Armenian_TSO
+XK_Armenian_tso
+XK_Armenian_VYUN
+XK_Armenian_vyun
+XK_Armenian_PYUR
+XK_Armenian_pyur
+XK_Armenian_KE
+XK_Armenian_ke
+XK_Armenian_O
+XK_Armenian_o
+XK_Armenian_FE
+XK_Armenian_fe
+XK_Armenian_apostrophe
+XK_Armenian_section_sign
+
+XK_Georgian_an
+XK_Georgian_ban
+XK_Georgian_gan
+XK_Georgian_don
+XK_Georgian_en
+XK_Georgian_vin
+XK_Georgian_zen
+XK_Georgian_tan
+XK_Georgian_in
+XK_Georgian_kan
+XK_Georgian_las
+XK_Georgian_man
+XK_Georgian_nar
+XK_Georgian_on
+XK_Georgian_par
+XK_Georgian_zhar
+XK_Georgian_rae
+XK_Georgian_san
+XK_Georgian_tar
+XK_Georgian_un
+XK_Georgian_phar
+XK_Georgian_khar
+XK_Georgian_ghan
+XK_Georgian_qar
+XK_Georgian_shin
+XK_Georgian_chin
+XK_Georgian_can
+XK_Georgian_jil
+XK_Georgian_cil
+XK_Georgian_char
+XK_Georgian_xan
+XK_Georgian_jhan
+XK_Georgian_hae
+XK_Georgian_he
+XK_Georgian_hie
+XK_Georgian_we
+XK_Georgian_har
+XK_Georgian_hoe
+XK_Georgian_fi
+
+XK_Ccedillaabovedot
+XK_Xabovedot
+XK_Qabovedot
+XK_Ibreve
+XK_IE
+XK_UO
+XK_Zstroke
+XK_Gcaron
+XK_Obarred
+XK_ccedillaabovedot
+XK_xabovedot
+XK_Ocaron
+XK_qabovedot
+XK_ibreve
+XK_ie
+XK_uo
+XK_zstroke
+XK_gcaron
+XK_ocaron
+XK_obarred
+XK_SCHWA
+XK_Lbelowdot
+XK_Lstrokebelowdot
+XK_Gtilde
+XK_lbelowdot
+XK_lstrokebelowdot
+XK_gtilde
+XK_schwa
+
+XK_Abelowdot
+XK_abelowdot
+XK_Ahook
+XK_ahook
+XK_Acircumflexacute
+XK_acircumflexacute
+XK_Acircumflexgrave
+XK_acircumflexgrave
+XK_Acircumflexhook
+XK_acircumflexhook
+XK_Acircumflextilde
+XK_acircumflextilde
+XK_Acircumflexbelowdot
+XK_acircumflexbelowdot
+XK_Abreveacute
+XK_abreveacute
+XK_Abrevegrave
+XK_abrevegrave
+XK_Abrevehook
+XK_abrevehook
+XK_Abrevetilde
+XK_abrevetilde
+XK_Abrevebelowdot
+XK_abrevebelowdot
+XK_Ebelowdot
+XK_ebelowdot
+XK_Ehook
+XK_ehook
+XK_Etilde
+XK_etilde
+XK_Ecircumflexacute
+XK_ecircumflexacute
+XK_Ecircumflexgrave
+XK_ecircumflexgrave
+XK_Ecircumflexhook
+XK_ecircumflexhook
+XK_Ecircumflextilde
+XK_ecircumflextilde
+XK_Ecircumflexbelowdot
+XK_ecircumflexbelowdot
+XK_Ihook
+XK_ihook
+XK_Ibelowdot
+XK_ibelowdot
+XK_Obelowdot
+XK_obelowdot
+XK_Ohook
+XK_ohook
+XK_Ocircumflexacute
+XK_ocircumflexacute
+XK_Ocircumflexgrave
+XK_ocircumflexgrave
+XK_Ocircumflexhook
+XK_ocircumflexhook
+XK_Ocircumflextilde
+XK_ocircumflextilde
+XK_Ocircumflexbelowdot
+XK_ocircumflexbelowdot
+XK_Ohornacute
+XK_ohornacute
+XK_Ohorngrave
+XK_ohorngrave
+XK_Ohornhook
+XK_ohornhook
+XK_Ohorntilde
+XK_ohorntilde
+XK_Ohornbelowdot
+XK_ohornbelowdot
+XK_Ubelowdot
+XK_ubelowdot
+XK_Uhook
+XK_uhook
+XK_Uhornacute
+XK_uhornacute
+XK_Uhorngrave
+XK_uhorngrave
+XK_Uhornhook
+XK_uhornhook
+XK_Uhorntilde
+XK_uhorntilde
+XK_Uhornbelowdot
+XK_uhornbelowdot
+XK_Ybelowdot
+XK_ybelowdot
+XK_Yhook
+XK_yhook
+XK_Ytilde
+XK_ytilde
+XK_Ohorn
+XK_ohorn
+XK_Uhorn
+XK_uhorn
+*/
+
+// map "Internet" keys to KeyIDs
+static const KeySym s_map1008FF[] =
+{
+ /* 0x00 */ 0, 0, kKeyBrightnessUp, kKeyBrightnessDown, 0, 0, 0, 0,
+ /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x10 */ 0, kKeyAudioDown, kKeyAudioMute, kKeyAudioUp,
+ /* 0x14 */ kKeyAudioPlay, kKeyAudioStop, kKeyAudioPrev, kKeyAudioNext,
+ /* 0x18 */ kKeyWWWHome, kKeyAppMail, 0, kKeyWWWSearch, 0, 0, 0, 0,
+ /* 0x20 */ 0, 0, 0, 0, 0, 0, kKeyWWWBack, kKeyWWWForward,
+ /* 0x28 */ kKeyWWWStop, kKeyWWWRefresh, 0, 0, kKeyEject, 0, 0, 0,
+ /* 0x30 */ kKeyWWWFavorites, 0, kKeyAppMedia, 0, 0, 0, 0, 0,
+ /* 0x38 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x40 */ kKeyAppUser1, kKeyAppUser2, 0, 0, 0, 0, 0, 0,
+ /* 0x48 */ 0, 0, kKeyMissionControl, kKeyLaunchpad, 0, 0, 0, 0,
+ /* 0x50 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x58 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x60 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x68 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x70 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x78 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x80 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x88 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x90 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0x98 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xa0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xa8 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xb0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xb8 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xc0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xc8 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xd0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xd8 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xe0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xe8 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xf0 */ 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 0xf8 */ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+//
+// XWindowsUtil
+//
+
+XWindowsUtil::KeySymMap XWindowsUtil::s_keySymToUCS4;
+
+bool
+XWindowsUtil::getWindowProperty(Display* display, Window window,
+ Atom property, String* data, Atom* type,
+ SInt32* format, bool deleteProperty)
+{
+ assert(display != NULL);
+
+ Atom actualType;
+ int actualDatumSize;
+
+ // ignore errors. XGetWindowProperty() will report failure.
+ XWindowsUtil::ErrorLock lock(display);
+
+ // read the property
+ bool okay = true;
+ const long length = XMaxRequestSize(display);
+ long offset = 0;
+ unsigned long bytesLeft = 1;
+ while (bytesLeft != 0) {
+ // get more data
+ unsigned long numItems;
+ unsigned char* rawData;
+ if (XGetWindowProperty(display, window, property,
+ offset, length, False, AnyPropertyType,
+ &actualType, &actualDatumSize,
+ &numItems, &bytesLeft, &rawData) != Success ||
+ actualType == None || actualDatumSize == 0) {
+ // failed
+ okay = false;
+ break;
+ }
+
+ // compute bytes read and advance offset
+ unsigned long numBytes;
+ switch (actualDatumSize) {
+ case 8:
+ default:
+ numBytes = numItems;
+ offset += numItems / 4;
+ break;
+
+ case 16:
+ numBytes = 2 * numItems;
+ offset += numItems / 2;
+ break;
+
+ case 32:
+ numBytes = 4 * numItems;
+ offset += numItems;
+ break;
+ }
+
+ // append data
+ if (data != NULL) {
+ data->append((char*)rawData, numBytes);
+ }
+ else {
+ // data is not required so don't try to get any more
+ bytesLeft = 0;
+ }
+
+ // done with returned data
+ XFree(rawData);
+ }
+
+ // delete the property if requested
+ if (deleteProperty) {
+ XDeleteProperty(display, window, property);
+ }
+
+ // save property info
+ if (type != NULL) {
+ *type = actualType;
+ }
+ if (format != NULL) {
+ *format = static_cast<SInt32>(actualDatumSize);
+ }
+
+ if (okay) {
+ LOG((CLOG_DEBUG2 "read property %d on window 0x%08x: bytes=%d", property, window, (data == NULL) ? 0 : data->size()));
+ return true;
+ }
+ else {
+ LOG((CLOG_DEBUG2 "can't read property %d on window 0x%08x", property, window));
+ return false;
+ }
+}
+
+bool
+XWindowsUtil::setWindowProperty(Display* display, Window window,
+ Atom property, const void* vdata, UInt32 size,
+ Atom type, SInt32 format)
+{
+ const UInt32 length = 4 * XMaxRequestSize(display);
+ const unsigned char* data = static_cast<const unsigned char*>(vdata);
+ UInt32 datumSize = static_cast<UInt32>(format / 8);
+ // format 32 on 64bit systems is 8 bytes not 4.
+ if (format == 32) {
+ datumSize = sizeof(Atom);
+ }
+
+ // save errors
+ bool error = false;
+ XWindowsUtil::ErrorLock lock(display, &error);
+
+ // how much data to send in first chunk?
+ UInt32 chunkSize = size;
+ if (chunkSize > length) {
+ chunkSize = length;
+ }
+
+ // send first chunk
+ XChangeProperty(display, window, property,
+ type, format, PropModeReplace,
+ data, chunkSize / datumSize);
+
+ // append remaining chunks
+ data += chunkSize;
+ size -= chunkSize;
+ while (!error && size > 0) {
+ chunkSize = size;
+ if (chunkSize > length) {
+ chunkSize = length;
+ }
+ XChangeProperty(display, window, property,
+ type, format, PropModeAppend,
+ data, chunkSize / datumSize);
+ data += chunkSize;
+ size -= chunkSize;
+ }
+
+ return !error;
+}
+
+Time
+XWindowsUtil::getCurrentTime(Display* display, Window window)
+{
+ XLockDisplay(display);
+ // select property events on window
+ XWindowAttributes attr;
+ XGetWindowAttributes(display, window, &attr);
+ XSelectInput(display, window, attr.your_event_mask | PropertyChangeMask);
+
+ // make a property name to receive dummy change
+ Atom atom = XInternAtom(display, "TIMESTAMP", False);
+
+ // do a zero-length append to get the current time
+ unsigned char dummy;
+ XChangeProperty(display, window, atom,
+ XA_INTEGER, 8,
+ PropModeAppend,
+ &dummy, 0);
+
+ // look for property notify events with the following
+ PropertyNotifyPredicateInfo filter;
+ filter.m_window = window;
+ filter.m_property = atom;
+
+ // wait for reply
+ XEvent xevent;
+ XIfEvent(display, &xevent, &XWindowsUtil::propertyNotifyPredicate,
+ (XPointer)&filter);
+ assert(xevent.type == PropertyNotify);
+ assert(xevent.xproperty.window == window);
+ assert(xevent.xproperty.atom == atom);
+
+ // restore event mask
+ XSelectInput(display, window, attr.your_event_mask);
+ XUnlockDisplay(display);
+
+ return xevent.xproperty.time;
+}
+
+KeyID
+XWindowsUtil::mapKeySymToKeyID(KeySym k)
+{
+ initKeyMaps();
+
+ switch (k & 0xffffff00) {
+ case 0x0000:
+ // Latin-1
+ return static_cast<KeyID>(k);
+
+ case 0xfe00:
+ // ISO 9995 Function and Modifier Keys
+ switch (k) {
+ case XK_ISO_Left_Tab:
+ return kKeyLeftTab;
+
+ case XK_ISO_Level3_Shift:
+ return kKeyAltGr;
+
+#ifdef XK_ISO_Level5_Shift
+ case XK_ISO_Level5_Shift:
+ return XK_ISO_Level5_Shift; //FIXME: there is no "usual" key for this...
+#endif
+
+ case XK_ISO_Next_Group:
+ return kKeyNextGroup;
+
+ case XK_ISO_Prev_Group:
+ return kKeyPrevGroup;
+
+ case XK_dead_grave:
+ return kKeyDeadGrave;
+
+ case XK_dead_acute:
+ return kKeyDeadAcute;
+
+ case XK_dead_circumflex:
+ return kKeyDeadCircumflex;
+
+ case XK_dead_tilde:
+ return kKeyDeadTilde;
+
+ case XK_dead_macron:
+ return kKeyDeadMacron;
+
+ case XK_dead_breve:
+ return kKeyDeadBreve;
+
+ case XK_dead_abovedot:
+ return kKeyDeadAbovedot;
+
+ case XK_dead_diaeresis:
+ return kKeyDeadDiaeresis;
+
+ case XK_dead_abovering:
+ return kKeyDeadAbovering;
+
+ case XK_dead_doubleacute:
+ return kKeyDeadDoubleacute;
+
+ case XK_dead_caron:
+ return kKeyDeadCaron;
+
+ case XK_dead_cedilla:
+ return kKeyDeadCedilla;
+
+ case XK_dead_ogonek:
+ return kKeyDeadOgonek;
+
+ default:
+ return kKeyNone;
+ }
+
+ case 0xff00:
+ // MISCELLANY
+ return static_cast<KeyID>(k - 0xff00 + 0xef00);
+
+ case 0x1008ff00:
+ // "Internet" keys
+ return s_map1008FF[k & 0xff];
+
+ default: {
+ // lookup character in table
+ KeySymMap::const_iterator index = s_keySymToUCS4.find(k);
+ if (index != s_keySymToUCS4.end()) {
+ return static_cast<KeyID>(index->second);
+ }
+
+ // unknown character
+ return kKeyNone;
+ }
+ }
+}
+
+UInt32
+XWindowsUtil::getModifierBitForKeySym(KeySym keysym)
+{
+ switch (keysym) {
+ case XK_Shift_L:
+ case XK_Shift_R:
+ return kKeyModifierBitShift;
+
+ case XK_Control_L:
+ case XK_Control_R:
+ return kKeyModifierBitControl;
+
+ case XK_Alt_L:
+ case XK_Alt_R:
+ return kKeyModifierBitAlt;
+
+ case XK_Meta_L:
+ case XK_Meta_R:
+ return kKeyModifierBitMeta;
+
+ case XK_Super_L:
+ case XK_Super_R:
+ case XK_Hyper_L:
+ case XK_Hyper_R:
+ return kKeyModifierBitSuper;
+
+ case XK_Mode_switch:
+ case XK_ISO_Level3_Shift:
+ return kKeyModifierBitAltGr;
+
+#ifdef XK_ISO_Level5_Shift
+ case XK_ISO_Level5_Shift:
+ return kKeyModifierBitLevel5Lock;
+#endif
+
+ case XK_Caps_Lock:
+ return kKeyModifierBitCapsLock;
+
+ case XK_Num_Lock:
+ return kKeyModifierBitNumLock;
+
+ case XK_Scroll_Lock:
+ return kKeyModifierBitScrollLock;
+
+ default:
+ return kKeyModifierBitNone;
+ }
+}
+
+String
+XWindowsUtil::atomToString(Display* display, Atom atom)
+{
+ if (atom == 0) {
+ return "None";
+ }
+
+ bool error = false;
+ XWindowsUtil::ErrorLock lock(display, &error);
+ char* name = XGetAtomName(display, atom);
+ if (error) {
+ return barrier::string::sprintf("<UNKNOWN> (%d)", (int)atom);
+ }
+ else {
+ String msg = barrier::string::sprintf("%s (%d)", name, (int)atom);
+ XFree(name);
+ return msg;
+ }
+}
+
+String
+XWindowsUtil::atomsToString(Display* display, const Atom* atom, UInt32 num)
+{
+ char** names = new char*[num];
+ bool error = false;
+ XWindowsUtil::ErrorLock lock(display, &error);
+ XGetAtomNames(display, const_cast<Atom*>(atom), (int)num, names);
+ String msg;
+ if (error) {
+ for (UInt32 i = 0; i < num; ++i) {
+ msg += barrier::string::sprintf("<UNKNOWN> (%d), ", (int)atom[i]);
+ }
+ }
+ else {
+ for (UInt32 i = 0; i < num; ++i) {
+ msg += barrier::string::sprintf("%s (%d), ", names[i], (int)atom[i]);
+ XFree(names[i]);
+ }
+ }
+ delete[] names;
+ if (msg.size() > 2) {
+ msg.erase(msg.size() - 2);
+ }
+ return msg;
+}
+
+void
+XWindowsUtil::convertAtomProperty(String& data)
+{
+ // as best i can tell, 64-bit systems don't pack Atoms into properties
+ // as 32-bit numbers but rather as the 64-bit numbers they are. that
+ // seems wrong but we have to cope. sometimes we'll get a list of
+ // atoms that's 8*n+4 bytes long, missing the trailing 4 bytes which
+ // should all be 0. since we're going to reference the Atoms as
+ // 64-bit numbers we have to ensure the last number is a full 64 bits.
+ if (sizeof(Atom) != 4 && ((data.size() / 4) & 1) != 0) {
+ UInt32 zero = 0;
+ data.append(reinterpret_cast<char*>(&zero), sizeof(zero));
+ }
+}
+
+void
+XWindowsUtil::appendAtomData(String& data, Atom atom)
+{
+ data.append(reinterpret_cast<char*>(&atom), sizeof(Atom));
+}
+
+void
+XWindowsUtil::replaceAtomData(String& data, UInt32 index, Atom atom)
+{
+ data.replace(index * sizeof(Atom), sizeof(Atom),
+ reinterpret_cast<const char*>(&atom),
+ sizeof(Atom));
+}
+
+void
+XWindowsUtil::appendTimeData(String& data, Time time)
+{
+ data.append(reinterpret_cast<char*>(&time), sizeof(Time));
+}
+
+Bool
+XWindowsUtil::propertyNotifyPredicate(Display*, XEvent* xevent, XPointer arg)
+{
+ PropertyNotifyPredicateInfo* filter =
+ reinterpret_cast<PropertyNotifyPredicateInfo*>(arg);
+ return (xevent->type == PropertyNotify &&
+ xevent->xproperty.window == filter->m_window &&
+ xevent->xproperty.atom == filter->m_property &&
+ xevent->xproperty.state == PropertyNewValue) ? True : False;
+}
+
+void
+XWindowsUtil::initKeyMaps()
+{
+ if (s_keySymToUCS4.empty()) {
+ for (size_t i =0; i < sizeof(s_keymap) / sizeof(s_keymap[0]); ++i) {
+ s_keySymToUCS4[s_keymap[i].keysym] = s_keymap[i].ucs4;
+ }
+ }
+}
+
+
+//
+// XWindowsUtil::ErrorLock
+//
+
+XWindowsUtil::ErrorLock* XWindowsUtil::ErrorLock::s_top = NULL;
+
+XWindowsUtil::ErrorLock::ErrorLock(Display* display) :
+ m_display(display)
+{
+ install(&XWindowsUtil::ErrorLock::ignoreHandler, NULL);
+}
+
+XWindowsUtil::ErrorLock::ErrorLock(Display* display, bool* flag) :
+ m_display(display)
+{
+ install(&XWindowsUtil::ErrorLock::saveHandler, flag);
+}
+
+XWindowsUtil::ErrorLock::ErrorLock(Display* display,
+ ErrorHandler handler, void* data) :
+ m_display(display)
+{
+ install(handler, data);
+}
+
+XWindowsUtil::ErrorLock::~ErrorLock()
+{
+ // make sure everything finishes before uninstalling handler
+ if (m_display != NULL) {
+ XSync(m_display, False);
+ }
+
+ // restore old handler
+ XSetErrorHandler(m_oldXHandler);
+ s_top = m_next;
+}
+
+void
+XWindowsUtil::ErrorLock::install(ErrorHandler handler, void* data)
+{
+ // make sure everything finishes before installing handler
+ if (m_display != NULL) {
+ XSync(m_display, False);
+ }
+
+ // install handler
+ m_handler = handler;
+ m_userData = data;
+ m_oldXHandler = XSetErrorHandler(
+ &XWindowsUtil::ErrorLock::internalHandler);
+ m_next = s_top;
+ s_top = this;
+}
+
+int
+XWindowsUtil::ErrorLock::internalHandler(Display* display, XErrorEvent* event)
+{
+ if (s_top != NULL && s_top->m_handler != NULL) {
+ s_top->m_handler(display, event, s_top->m_userData);
+ }
+ return 0;
+}
+
+void
+XWindowsUtil::ErrorLock::ignoreHandler(Display*, XErrorEvent* e, void*)
+{
+ LOG((CLOG_DEBUG1 "ignoring X error: %d", e->error_code));
+}
+
+void
+XWindowsUtil::ErrorLock::saveHandler(Display* display, XErrorEvent* e, void* flag)
+{
+ char errtxt[1024];
+ XGetErrorText(display, e->error_code, errtxt, 1023);
+ LOG((CLOG_DEBUG1 "flagging X error: %d - %.1023s", e->error_code, errtxt));
+ *static_cast<bool*>(flag) = true;
+}
diff --git a/src/lib/platform/XWindowsUtil.h b/src/lib/platform/XWindowsUtil.h
new file mode 100644
index 0000000..4df888f
--- /dev/null
+++ b/src/lib/platform/XWindowsUtil.h
@@ -0,0 +1,187 @@
+/*
+ * barrier -- 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"
+#include "base/EventTypes.h"
+#include "common/stdmap.h"
+#include "common/stdvector.h"
+
+#if X_DISPLAY_MISSING
+# error X11 is required to build barrier
+#else
+# include <X11/Xlib.h>
+#endif
+
+//! X11 utility functions
+class XWindowsUtil {
+public:
+ typedef std::vector<KeySym> KeySyms;
+
+ //! Get property
+ /*!
+ Gets property \c property on \c window. \b Appends the data to
+ \c *data if \c data is not NULL, saves the property type in \c *type
+ if \c type is not NULL, and saves the property format in \c *format
+ if \c format is not NULL. If \c deleteProperty is true then the
+ property is deleted after being read.
+ */
+ static bool getWindowProperty(Display*,
+ Window window, Atom property,
+ String* data, Atom* type,
+ SInt32* format, bool deleteProperty);
+
+ //! Set property
+ /*!
+ Sets property \c property on \c window to \c size bytes of data from
+ \c data.
+ */
+ static bool setWindowProperty(Display*,
+ Window window, Atom property,
+ const void* data, UInt32 size,
+ Atom type, SInt32 format);
+
+ //! Get X server time
+ /*!
+ Returns the current X server time.
+ */
+ static Time getCurrentTime(Display*, Window);
+
+ //! Convert KeySym to KeyID
+ /*!
+ Converts a KeySym to the equivalent KeyID. Returns kKeyNone if the
+ KeySym cannot be mapped.
+ */
+ static UInt32 mapKeySymToKeyID(KeySym);
+
+ //! Convert KeySym to corresponding KeyModifierMask
+ /*!
+ Converts a KeySym to the corresponding KeyModifierMask, or 0 if the
+ KeySym is not a modifier.
+ */
+ static UInt32 getModifierBitForKeySym(KeySym keysym);
+
+ //! Convert Atom to its string
+ /*!
+ Converts \p atom to its string representation.
+ */
+ static String atomToString(Display*, Atom atom);
+
+ //! Convert several Atoms to a string
+ /*!
+ Converts each atom in \p atoms to its string representation and
+ concatenates the results.
+ */
+ static String atomsToString(Display* display,
+ const Atom* atom, UInt32 num);
+
+ //! Prepare a property of atoms for use
+ /*!
+ 64-bit systems may need to modify a property's data if it's a
+ list of Atoms before using it.
+ */
+ static void convertAtomProperty(String& data);
+
+ //! Append an Atom to property data
+ /*!
+ Converts \p atom to a 32-bit on-the-wire format and appends it to
+ \p data.
+ */
+ static void appendAtomData(String& data, Atom atom);
+
+ //! Replace an Atom in property data
+ /*!
+ Converts \p atom to a 32-bit on-the-wire format and replaces the atom
+ at index \p index in \p data.
+ */
+ static void replaceAtomData(String& data,
+ UInt32 index, Atom atom);
+
+ //! Append an Time to property data
+ /*!
+ Converts \p time to a 32-bit on-the-wire format and appends it to
+ \p data.
+ */
+ static void appendTimeData(String& data, Time time);
+
+ //! X11 error handler
+ /*!
+ This class sets an X error handler in the c'tor and restores the
+ previous error handler in the d'tor. A lock should only be
+ installed while the display is locked by the thread.
+
+ ErrorLock() ignores errors
+ ErrorLock(bool* flag) sets *flag to true if any error occurs
+ */
+ class ErrorLock {
+ public:
+ //! Error handler type
+ typedef void (*ErrorHandler)(Display*, XErrorEvent*, void* userData);
+
+ /*!
+ Ignore X11 errors.
+ */
+ ErrorLock(Display*);
+
+ /*!
+ Set \c *errorFlag if any error occurs.
+ */
+ ErrorLock(Display*, bool* errorFlag);
+
+ /*!
+ Call \c handler on each error.
+ */
+ ErrorLock(Display*, ErrorHandler handler, void* userData);
+
+ ~ErrorLock();
+
+ private:
+ void install(ErrorHandler, void*);
+ static int internalHandler(Display*, XErrorEvent*);
+ static void ignoreHandler(Display*, XErrorEvent*, void*);
+ static void saveHandler(Display*, XErrorEvent*, void*);
+
+ private:
+ typedef int (*XErrorHandler)(Display*, XErrorEvent*);
+
+ Display* m_display;
+ ErrorHandler m_handler;
+ void* m_userData;
+ XErrorHandler m_oldXHandler;
+ ErrorLock* m_next;
+ static ErrorLock* s_top;
+ };
+
+private:
+ class PropertyNotifyPredicateInfo {
+ public:
+ Window m_window;
+ Atom m_property;
+ };
+
+ static Bool propertyNotifyPredicate(Display*,
+ XEvent* xevent, XPointer arg);
+
+ static void initKeyMaps();
+
+private:
+ typedef std::map<KeySym, UInt32> KeySymMap;
+
+ static KeySymMap s_keySymToUCS4;
+};
diff --git a/src/lib/platform/synwinhk.h b/src/lib/platform/synwinhk.h
new file mode 100644
index 0000000..4b2d8e3
--- /dev/null
+++ b/src/lib/platform/synwinhk.h
@@ -0,0 +1,66 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2018 Debauchee Open Source Group
+ * 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/EventTypes.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+#if defined(synwinhk_EXPORTS)
+#define CBARRIERHOOK_API __declspec(dllexport)
+#else
+#define CBARRIERHOOK_API __declspec(dllimport)
+#endif
+
+#define BARRIER_MSG_MARK WM_APP + 0x0011 // mark id; <unused>
+#define BARRIER_MSG_KEY WM_APP + 0x0012 // vk code; key data
+#define BARRIER_MSG_MOUSE_BUTTON WM_APP + 0x0013 // button msg; <unused>
+#define BARRIER_MSG_MOUSE_WHEEL WM_APP + 0x0014 // delta; <unused>
+#define BARRIER_MSG_MOUSE_MOVE WM_APP + 0x0015 // x; y
+#define BARRIER_MSG_POST_WARP WM_APP + 0x0016 // <unused>; <unused>
+#define BARRIER_MSG_PRE_WARP WM_APP + 0x0017 // x; y
+#define BARRIER_MSG_SCREEN_SAVER WM_APP + 0x0018 // activated; <unused>
+#define BARRIER_MSG_DEBUG WM_APP + 0x0019 // data, data
+#define BARRIER_MSG_INPUT_FIRST BARRIER_MSG_KEY
+#define BARRIER_MSG_INPUT_LAST BARRIER_MSG_PRE_WARP
+#define BARRIER_HOOK_LAST_MSG BARRIER_MSG_DEBUG
+
+#define BARRIER_HOOK_FAKE_INPUT_VIRTUAL_KEY VK_CANCEL
+#define BARRIER_HOOK_FAKE_INPUT_SCANCODE 0
+
+extern "C" {
+
+enum EHookMode {
+ kHOOK_DISABLE,
+ kHOOK_WATCH_JUMP_ZONE,
+ kHOOK_RELAY_EVENTS
+};
+
+/* REMOVED ImmuneKeys for migration of synwinhk out of DLL
+
+typedef void (*SetImmuneKeysFunc)(const DWORD*, std::size_t);
+
+// do not call setImmuneKeys() while the hooks are active!
+CBARRIERHOOK_API void setImmuneKeys(const DWORD *list, std::size_t size);
+
+*/
+
+}
diff --git a/src/lib/server/BaseClientProxy.cpp b/src/lib/server/BaseClientProxy.cpp
new file mode 100644
index 0000000..b9c5339
--- /dev/null
+++ b/src/lib/server/BaseClientProxy.cpp
@@ -0,0 +1,56 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2006 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 "server/BaseClientProxy.h"
+
+//
+// BaseClientProxy
+//
+
+BaseClientProxy::BaseClientProxy(const String& name) :
+ m_name(name),
+ m_x(0),
+ m_y(0)
+{
+ // do nothing
+}
+
+BaseClientProxy::~BaseClientProxy()
+{
+ // do nothing
+}
+
+void
+BaseClientProxy::setJumpCursorPos(SInt32 x, SInt32 y)
+{
+ m_x = x;
+ m_y = y;
+}
+
+void
+BaseClientProxy::getJumpCursorPos(SInt32& x, SInt32& y) const
+{
+ x = m_x;
+ y = m_y;
+}
+
+String
+BaseClientProxy::getName() const
+{
+ return m_name;
+}
diff --git a/src/lib/server/BaseClientProxy.h b/src/lib/server/BaseClientProxy.h
new file mode 100644
index 0000000..c7c23ff
--- /dev/null
+++ b/src/lib/server/BaseClientProxy.h
@@ -0,0 +1,99 @@
+/*
+ * barrier -- 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 "barrier/IClient.h"
+#include "base/String.h"
+
+namespace barrier { class IStream; }
+
+//! Generic proxy for client or primary
+class BaseClientProxy : public IClient {
+public:
+ /*!
+ \c name is the name of the client.
+ */
+ BaseClientProxy(const String& name);
+ ~BaseClientProxy();
+
+ //! @name manipulators
+ //@{
+
+ //! Save cursor position
+ /*!
+ Save the position of the cursor when jumping from client.
+ */
+ void setJumpCursorPos(SInt32 x, SInt32 y);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get cursor position
+ /*!
+ Get the position of the cursor when last jumping from client.
+ */
+ void getJumpCursorPos(SInt32& x, SInt32& y) const;
+
+ //! Get cursor position
+ /*!
+ Return if this proxy is for client or primary.
+ */
+ virtual bool isPrimary() const { return false; }
+
+ //@}
+
+ // IScreen
+ virtual void* getEventTarget() const = 0;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const = 0;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
+
+ // IClient overrides
+ virtual void enter(SInt32 xAbs, SInt32 yAbs,
+ UInt32 seqNum, KeyModifierMask mask,
+ bool forScreensaver) = 0;
+ virtual bool leave() = 0;
+ 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 keyRepeat(KeyID, KeyModifierMask,
+ SInt32 count, KeyButton) = 0;
+ virtual void keyUp(KeyID, KeyModifierMask, KeyButton) = 0;
+ virtual void mouseDown(ButtonID) = 0;
+ virtual void mouseUp(ButtonID) = 0;
+ virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0;
+ virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0;
+ virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta) = 0;
+ virtual void screensaver(bool activate) = 0;
+ virtual void resetOptions() = 0;
+ virtual void setOptions(const OptionsList& options) = 0;
+ virtual void sendDragInfo(UInt32 fileCount, const char* info,
+ size_t size) = 0;
+ virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize) = 0;
+ virtual String getName() const;
+ virtual barrier::IStream*
+ getStream() const = 0;
+
+private:
+ String m_name;
+ SInt32 m_x, m_y;
+};
diff --git a/src/lib/server/CMakeLists.txt b/src/lib/server/CMakeLists.txt
new file mode 100644
index 0000000..5242d6d
--- /dev/null
+++ b/src/lib/server/CMakeLists.txt
@@ -0,0 +1,30 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB headers "*.h")
+file(GLOB sources "*.cpp")
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_library(server STATIC ${sources})
+
+target_link_libraries(server)
+
+if (UNIX)
+ target_link_libraries(server synlib)
+endif()
diff --git a/src/lib/server/ClientListener.cpp b/src/lib/server/ClientListener.cpp
new file mode 100644
index 0000000..00067ba
--- /dev/null
+++ b/src/lib/server/ClientListener.cpp
@@ -0,0 +1,256 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "server/ClientListener.h"
+
+#include "server/ClientProxy.h"
+#include "server/ClientProxyUnknown.h"
+#include "barrier/PacketStreamFilter.h"
+#include "net/IDataSocket.h"
+#include "net/IListenSocket.h"
+#include "net/ISocketFactory.h"
+#include "net/XSocket.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+
+//
+// ClientListener
+//
+
+ClientListener::ClientListener(const NetworkAddress& address,
+ ISocketFactory* socketFactory,
+ IEventQueue* events,
+ bool enableCrypto) :
+ m_socketFactory(socketFactory),
+ m_server(NULL),
+ m_events(events),
+ m_useSecureNetwork(enableCrypto)
+{
+ assert(m_socketFactory != NULL);
+
+ try {
+ m_listen = m_socketFactory->createListen(
+ ARCH->getAddrFamily(address.getAddress()),
+ m_useSecureNetwork);
+
+ // setup event handler
+ m_events->adoptHandler(m_events->forIListenSocket().connecting(),
+ m_listen,
+ new TMethodEventJob<ClientListener>(this,
+ &ClientListener::handleClientConnecting));
+
+ // bind listen address
+ LOG((CLOG_DEBUG1 "binding listen socket"));
+ m_listen->bind(address);
+ }
+ catch (XSocketAddressInUse&) {
+ cleanupListenSocket();
+ delete m_socketFactory;
+ throw;
+ }
+ catch (XBase&) {
+ cleanupListenSocket();
+ delete m_socketFactory;
+ throw;
+ }
+ LOG((CLOG_DEBUG1 "listening for clients"));
+}
+
+ClientListener::~ClientListener()
+{
+ LOG((CLOG_DEBUG1 "stop listening for clients"));
+
+ // discard already connected clients
+ for (NewClients::iterator index = m_newClients.begin();
+ index != m_newClients.end(); ++index) {
+ ClientProxyUnknown* client = *index;
+ m_events->removeHandler(
+ m_events->forClientProxyUnknown().success(), client);
+ m_events->removeHandler(
+ m_events->forClientProxyUnknown().failure(), client);
+ m_events->removeHandler(
+ m_events->forClientProxy().disconnected(), client);
+ delete client;
+ }
+
+ // discard waiting clients
+ ClientProxy* client = getNextClient();
+ while (client != NULL) {
+ delete client;
+ client = getNextClient();
+ }
+
+ m_events->removeHandler(m_events->forIListenSocket().connecting(), m_listen);
+ cleanupListenSocket();
+ cleanupClientSockets();
+ delete m_socketFactory;
+}
+
+void
+ClientListener::setServer(Server* server)
+{
+ assert(server != NULL);
+ m_server = server;
+}
+
+ClientProxy*
+ClientListener::getNextClient()
+{
+ ClientProxy* client = NULL;
+ if (!m_waitingClients.empty()) {
+ client = m_waitingClients.front();
+ m_waitingClients.pop_front();
+ m_events->removeHandler(m_events->forClientProxy().disconnected(), client);
+ }
+ return client;
+}
+
+void
+ClientListener::handleClientConnecting(const Event&, void*)
+{
+ // accept client connection
+ IDataSocket* socket = m_listen->accept();
+
+ if (socket == NULL) {
+ return;
+ }
+
+ m_clientSockets.insert(socket);
+
+ m_events->adoptHandler(m_events->forClientListener().accepted(),
+ socket->getEventTarget(),
+ new TMethodEventJob<ClientListener>(this,
+ &ClientListener::handleClientAccepted, socket));
+
+ // When using non SSL, server accepts clients immediately, while SSL
+ // has to call secure accept which may require retry
+ if (!m_useSecureNetwork) {
+ m_events->addEvent(Event(m_events->forClientListener().accepted(),
+ socket->getEventTarget()));
+ }
+}
+
+void
+ClientListener::handleClientAccepted(const Event&, void* vsocket)
+{
+ LOG((CLOG_NOTE "accepted client connection"));
+
+ IDataSocket* socket = static_cast<IDataSocket*>(vsocket);
+
+ // filter socket messages, including a packetizing filter
+ barrier::IStream* stream = new PacketStreamFilter(m_events, socket, false);
+ assert(m_server != NULL);
+
+ // create proxy for unknown client
+ ClientProxyUnknown* client = new ClientProxyUnknown(stream, 30.0, m_server, m_events);
+
+ m_newClients.insert(client);
+
+ // watch for events from unknown client
+ m_events->adoptHandler(m_events->forClientProxyUnknown().success(),
+ client,
+ new TMethodEventJob<ClientListener>(this,
+ &ClientListener::handleUnknownClient, client));
+ m_events->adoptHandler(m_events->forClientProxyUnknown().failure(),
+ client,
+ new TMethodEventJob<ClientListener>(this,
+ &ClientListener::handleUnknownClient, client));
+}
+
+void
+ClientListener::handleUnknownClient(const Event&, void* vclient)
+{
+ ClientProxyUnknown* unknownClient =
+ static_cast<ClientProxyUnknown*>(vclient);
+
+ // we should have the client in our new client list
+ assert(m_newClients.count(unknownClient) == 1);
+
+ // get the real client proxy and install it
+ ClientProxy* client = unknownClient->orphanClientProxy();
+ bool handshakeOk = true;
+ if (client != NULL) {
+ // handshake was successful
+ m_waitingClients.push_back(client);
+ m_events->addEvent(Event(m_events->forClientListener().connected(),
+ this));
+
+ // watch for client to disconnect while it's in our queue
+ m_events->adoptHandler(m_events->forClientProxy().disconnected(), client,
+ new TMethodEventJob<ClientListener>(this,
+ &ClientListener::handleClientDisconnected,
+ client));
+ }
+ else {
+ handshakeOk = false;
+ }
+
+ // now finished with unknown client
+ m_events->removeHandler(m_events->forClientProxyUnknown().success(), client);
+ m_events->removeHandler(m_events->forClientProxyUnknown().failure(), client);
+ m_newClients.erase(unknownClient);
+ PacketStreamFilter* streamFileter = dynamic_cast<PacketStreamFilter*>(unknownClient->getStream());
+ IDataSocket* socket = NULL;
+ if (streamFileter != NULL) {
+ socket = dynamic_cast<IDataSocket*>(streamFileter->getStream());
+ }
+
+ delete unknownClient;
+}
+
+void
+ClientListener::handleClientDisconnected(const Event&, void* vclient)
+{
+ ClientProxy* client = static_cast<ClientProxy*>(vclient);
+
+ // find client in waiting clients queue
+ for (WaitingClients::iterator i = m_waitingClients.begin(),
+ n = m_waitingClients.end(); i != n; ++i) {
+ if (*i == client) {
+ m_waitingClients.erase(i);
+ m_events->removeHandler(m_events->forClientProxy().disconnected(),
+ client);
+
+ // pull out the socket before deleting the client so
+ // we know which socket we no longer need
+ IDataSocket* socket = static_cast<IDataSocket*>(client->getStream());
+ delete client;
+ m_clientSockets.erase(socket);
+ delete socket;
+
+ break;
+ }
+ }
+}
+
+void
+ClientListener::cleanupListenSocket()
+{
+ delete m_listen;
+}
+
+void
+ClientListener::cleanupClientSockets()
+{
+ ClientSockets::iterator it;
+ for (it = m_clientSockets.begin(); it != m_clientSockets.end(); it++) {
+ delete *it;
+ }
+ m_clientSockets.clear();
+}
diff --git a/src/lib/server/ClientListener.h b/src/lib/server/ClientListener.h
new file mode 100644
index 0000000..b02cbb1
--- /dev/null
+++ b/src/lib/server/ClientListener.h
@@ -0,0 +1,91 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "server/Config.h"
+#include "base/EventTypes.h"
+#include "base/Event.h"
+#include "common/stddeque.h"
+#include "common/stdset.h"
+
+class ClientProxy;
+class ClientProxyUnknown;
+class NetworkAddress;
+class IListenSocket;
+class ISocketFactory;
+class Server;
+class IEventQueue;
+class IDataSocket;
+
+class ClientListener {
+public:
+ // The factories are adopted.
+ ClientListener(const NetworkAddress&,
+ ISocketFactory*,
+ IEventQueue* events,
+ bool enableCrypto);
+ ~ClientListener();
+
+ //! @name manipulators
+ //@{
+
+ void setServer(Server* server);
+
+ //@}
+
+ //! @name accessors
+ //@{
+
+ //! Get next connected client
+ /*!
+ Returns the next connected client and removes it from the internal
+ list. The client is responsible for deleting the returned client.
+ Returns NULL if no clients are available.
+ */
+ ClientProxy* getNextClient();
+
+ //! Get server which owns this listener
+ Server* getServer() { return m_server; }
+
+ //@}
+
+private:
+ // client connection event handlers
+ void handleClientConnecting(const Event&, void*);
+ void handleClientAccepted(const Event&, void*);
+ void handleUnknownClient(const Event&, void*);
+ void handleClientDisconnected(const Event&, void*);
+
+ void cleanupListenSocket();
+ void cleanupClientSockets();
+
+private:
+ typedef std::set<ClientProxyUnknown*> NewClients;
+ typedef std::deque<ClientProxy*> WaitingClients;
+ typedef std::set<IDataSocket*> ClientSockets;
+
+ IListenSocket* m_listen;
+ ISocketFactory* m_socketFactory;
+ NewClients m_newClients;
+ WaitingClients m_waitingClients;
+ Server* m_server;
+ IEventQueue* m_events;
+ bool m_useSecureNetwork;
+ ClientSockets m_clientSockets;
+};
diff --git a/src/lib/server/ClientProxy.cpp b/src/lib/server/ClientProxy.cpp
new file mode 100644
index 0000000..5a28248
--- /dev/null
+++ b/src/lib/server/ClientProxy.cpp
@@ -0,0 +1,61 @@
+/*
+ * barrier -- 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 "server/ClientProxy.h"
+
+#include "barrier/ProtocolUtil.h"
+#include "io/IStream.h"
+#include "base/Log.h"
+#include "base/EventQueue.h"
+
+//
+// ClientProxy
+//
+
+ClientProxy::ClientProxy(const String& name, barrier::IStream* stream) :
+ BaseClientProxy(name),
+ m_stream(stream)
+{
+}
+
+ClientProxy::~ClientProxy()
+{
+ delete m_stream;
+}
+
+void
+ClientProxy::close(const char* msg)
+{
+ LOG((CLOG_DEBUG1 "send close \"%s\" to \"%s\"", msg, getName().c_str()));
+ ProtocolUtil::writef(getStream(), msg);
+
+ // force the close to be sent before we return
+ getStream()->flush();
+}
+
+barrier::IStream*
+ClientProxy::getStream() const
+{
+ return m_stream;
+}
+
+void*
+ClientProxy::getEventTarget() const
+{
+ return static_cast<IScreen*>(const_cast<ClientProxy*>(this));
+}
diff --git a/src/lib/server/ClientProxy.h b/src/lib/server/ClientProxy.h
new file mode 100644
index 0000000..726ded4
--- /dev/null
+++ b/src/lib/server/ClientProxy.h
@@ -0,0 +1,91 @@
+/*
+ * barrier -- 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 "server/BaseClientProxy.h"
+#include "base/Event.h"
+#include "base/String.h"
+#include "base/EventTypes.h"
+
+namespace barrier { class IStream; }
+
+//! Generic proxy for client
+class ClientProxy : public BaseClientProxy {
+public:
+ /*!
+ \c name is the name of the client.
+ */
+ ClientProxy(const String& name, barrier::IStream* adoptedStream);
+ ~ClientProxy();
+
+ //! @name manipulators
+ //@{
+
+ //! Disconnect
+ /*!
+ Ask the client to disconnect, using \p msg as the reason.
+ */
+ void close(const char* msg);
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get stream
+ /*!
+ Returns the original stream passed to the c'tor.
+ */
+ barrier::IStream* getStream() const;
+
+ //@}
+
+ // IScreen
+ virtual void* getEventTarget() const;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const = 0;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const = 0;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const = 0;
+
+ // IClient overrides
+ virtual void enter(SInt32 xAbs, SInt32 yAbs,
+ UInt32 seqNum, KeyModifierMask mask,
+ bool forScreensaver) = 0;
+ virtual bool leave() = 0;
+ 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 keyRepeat(KeyID, KeyModifierMask,
+ SInt32 count, KeyButton) = 0;
+ virtual void keyUp(KeyID, KeyModifierMask, KeyButton) = 0;
+ virtual void mouseDown(ButtonID) = 0;
+ virtual void mouseUp(ButtonID) = 0;
+ virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0;
+ virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel) = 0;
+ virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta) = 0;
+ virtual void screensaver(bool activate) = 0;
+ virtual void resetOptions() = 0;
+ virtual void setOptions(const OptionsList& options) = 0;
+ virtual void sendDragInfo(UInt32 fileCount, const char* info,
+ size_t size) = 0;
+ virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize) = 0;
+
+private:
+ barrier::IStream* m_stream;
+};
diff --git a/src/lib/server/ClientProxy1_0.cpp b/src/lib/server/ClientProxy1_0.cpp
new file mode 100644
index 0000000..ee805c6
--- /dev/null
+++ b/src/lib/server/ClientProxy1_0.cpp
@@ -0,0 +1,484 @@
+/*
+ * barrier -- 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 "server/ClientProxy1_0.h"
+
+#include "barrier/ProtocolUtil.h"
+#include "barrier/XBarrier.h"
+#include "io/IStream.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+
+#include <cstring>
+
+//
+// ClientProxy1_0
+//
+
+ClientProxy1_0::ClientProxy1_0(const String& name, barrier::IStream* stream, IEventQueue* events) :
+ ClientProxy(name, stream),
+ m_heartbeatTimer(NULL),
+ m_parser(&ClientProxy1_0::parseHandshakeMessage),
+ m_events(events)
+{
+ // install event handlers
+ m_events->adoptHandler(m_events->forIStream().inputReady(),
+ stream->getEventTarget(),
+ new TMethodEventJob<ClientProxy1_0>(this,
+ &ClientProxy1_0::handleData, NULL));
+ m_events->adoptHandler(m_events->forIStream().outputError(),
+ stream->getEventTarget(),
+ new TMethodEventJob<ClientProxy1_0>(this,
+ &ClientProxy1_0::handleWriteError, NULL));
+ m_events->adoptHandler(m_events->forIStream().inputShutdown(),
+ stream->getEventTarget(),
+ new TMethodEventJob<ClientProxy1_0>(this,
+ &ClientProxy1_0::handleDisconnect, NULL));
+ m_events->adoptHandler(m_events->forIStream().outputShutdown(),
+ stream->getEventTarget(),
+ new TMethodEventJob<ClientProxy1_0>(this,
+ &ClientProxy1_0::handleWriteError, NULL));
+ m_events->adoptHandler(Event::kTimer, this,
+ new TMethodEventJob<ClientProxy1_0>(this,
+ &ClientProxy1_0::handleFlatline, NULL));
+
+ setHeartbeatRate(kHeartRate, kHeartRate * kHeartBeatsUntilDeath);
+
+ LOG((CLOG_DEBUG1 "querying client \"%s\" info", getName().c_str()));
+ ProtocolUtil::writef(getStream(), kMsgQInfo);
+}
+
+ClientProxy1_0::~ClientProxy1_0()
+{
+ removeHandlers();
+}
+
+void
+ClientProxy1_0::disconnect()
+{
+ removeHandlers();
+ getStream()->close();
+ m_events->addEvent(Event(m_events->forClientProxy().disconnected(), getEventTarget()));
+}
+
+void
+ClientProxy1_0::removeHandlers()
+{
+ // uninstall event handlers
+ m_events->removeHandler(m_events->forIStream().inputReady(),
+ getStream()->getEventTarget());
+ m_events->removeHandler(m_events->forIStream().outputError(),
+ getStream()->getEventTarget());
+ m_events->removeHandler(m_events->forIStream().inputShutdown(),
+ getStream()->getEventTarget());
+ m_events->removeHandler(m_events->forIStream().outputShutdown(),
+ getStream()->getEventTarget());
+ m_events->removeHandler(Event::kTimer, this);
+
+ // remove timer
+ removeHeartbeatTimer();
+}
+
+void
+ClientProxy1_0::addHeartbeatTimer()
+{
+ if (m_heartbeatAlarm > 0.0) {
+ m_heartbeatTimer = m_events->newOneShotTimer(m_heartbeatAlarm, this);
+ }
+}
+
+void
+ClientProxy1_0::removeHeartbeatTimer()
+{
+ if (m_heartbeatTimer != NULL) {
+ m_events->deleteTimer(m_heartbeatTimer);
+ m_heartbeatTimer = NULL;
+ }
+}
+
+void
+ClientProxy1_0::resetHeartbeatTimer()
+{
+ // reset the alarm
+ removeHeartbeatTimer();
+ addHeartbeatTimer();
+}
+
+void
+ClientProxy1_0::resetHeartbeatRate()
+{
+ setHeartbeatRate(kHeartRate, kHeartRate * kHeartBeatsUntilDeath);
+}
+
+void
+ClientProxy1_0::setHeartbeatRate(double, double alarm)
+{
+ m_heartbeatAlarm = alarm;
+}
+
+void
+ClientProxy1_0::handleData(const Event&, void*)
+{
+ // handle messages until there are no more. first read message code.
+ UInt8 code[4];
+ UInt32 n = getStream()->read(code, 4);
+ while (n != 0) {
+ // verify we got an entire code
+ if (n != 4) {
+ LOG((CLOG_ERR "incomplete message from \"%s\": %d bytes", getName().c_str(), n));
+ disconnect();
+ return;
+ }
+
+ // parse message
+ LOG((CLOG_DEBUG2 "msg from \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3]));
+ if (!(this->*m_parser)(code)) {
+ LOG((CLOG_ERR "invalid message from client \"%s\": %c%c%c%c", getName().c_str(), code[0], code[1], code[2], code[3]));
+ disconnect();
+ return;
+ }
+
+ // next message
+ n = getStream()->read(code, 4);
+ }
+
+ // restart heartbeat timer
+ resetHeartbeatTimer();
+}
+
+bool
+ClientProxy1_0::parseHandshakeMessage(const UInt8* code)
+{
+ if (memcmp(code, kMsgCNoop, 4) == 0) {
+ // discard no-ops
+ LOG((CLOG_DEBUG2 "no-op from", getName().c_str()));
+ return true;
+ }
+ else if (memcmp(code, kMsgDInfo, 4) == 0) {
+ // future messages get parsed by parseMessage
+ m_parser = &ClientProxy1_0::parseMessage;
+ if (recvInfo()) {
+ m_events->addEvent(Event(m_events->forClientProxy().ready(), getEventTarget()));
+ addHeartbeatTimer();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+ClientProxy1_0::parseMessage(const UInt8* code)
+{
+ if (memcmp(code, kMsgDInfo, 4) == 0) {
+ if (recvInfo()) {
+ m_events->addEvent(
+ Event(m_events->forIScreen().shapeChanged(), getEventTarget()));
+ return true;
+ }
+ return false;
+ }
+ else if (memcmp(code, kMsgCNoop, 4) == 0) {
+ // discard no-ops
+ LOG((CLOG_DEBUG2 "no-op from", getName().c_str()));
+ return true;
+ }
+ else if (memcmp(code, kMsgCClipboard, 4) == 0) {
+ return recvGrabClipboard();
+ }
+ else if (memcmp(code, kMsgDClipboard, 4) == 0) {
+ return recvClipboard();
+ }
+ return false;
+}
+
+void
+ClientProxy1_0::handleDisconnect(const Event&, void*)
+{
+ LOG((CLOG_NOTE "client \"%s\" has disconnected", getName().c_str()));
+ disconnect();
+}
+
+void
+ClientProxy1_0::handleWriteError(const Event&, void*)
+{
+ LOG((CLOG_WARN "error writing to client \"%s\"", getName().c_str()));
+ disconnect();
+}
+
+void
+ClientProxy1_0::handleFlatline(const Event&, void*)
+{
+ // didn't get a heartbeat fast enough. assume client is dead.
+ LOG((CLOG_NOTE "client \"%s\" is dead", getName().c_str()));
+ disconnect();
+}
+
+bool
+ClientProxy1_0::getClipboard(ClipboardID id, IClipboard* clipboard) const
+{
+ Clipboard::copy(clipboard, &m_clipboard[id].m_clipboard);
+ return true;
+}
+
+void
+ClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
+{
+ x = m_info.m_x;
+ y = m_info.m_y;
+ w = m_info.m_w;
+ h = m_info.m_h;
+}
+
+void
+ClientProxy1_0::getCursorPos(SInt32& x, SInt32& y) const
+{
+ // note -- this returns the cursor pos from when we last got client info
+ x = m_info.m_mx;
+ y = m_info.m_my;
+}
+
+void
+ClientProxy1_0::enter(SInt32 xAbs, SInt32 yAbs,
+ UInt32 seqNum, KeyModifierMask mask, bool)
+{
+ LOG((CLOG_DEBUG1 "send enter to \"%s\", %d,%d %d %04x", getName().c_str(), xAbs, yAbs, seqNum, mask));
+ ProtocolUtil::writef(getStream(), kMsgCEnter,
+ xAbs, yAbs, seqNum, mask);
+}
+
+bool
+ClientProxy1_0::leave()
+{
+ LOG((CLOG_DEBUG1 "send leave to \"%s\"", getName().c_str()));
+ ProtocolUtil::writef(getStream(), kMsgCLeave);
+
+ // we can never prevent the user from leaving
+ return true;
+}
+
+void
+ClientProxy1_0::setClipboard(ClipboardID id, const IClipboard* clipboard)
+{
+ // ignore -- deprecated in protocol 1.0
+}
+
+void
+ClientProxy1_0::grabClipboard(ClipboardID id)
+{
+ LOG((CLOG_DEBUG "send grab clipboard %d to \"%s\"", id, getName().c_str()));
+ ProtocolUtil::writef(getStream(), kMsgCClipboard, id, 0);
+
+ // this clipboard is now dirty
+ m_clipboard[id].m_dirty = true;
+}
+
+void
+ClientProxy1_0::setClipboardDirty(ClipboardID id, bool dirty)
+{
+ m_clipboard[id].m_dirty = dirty;
+}
+
+void
+ClientProxy1_0::keyDown(KeyID key, KeyModifierMask mask, KeyButton)
+{
+ 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);
+}
+
+void
+ClientProxy1_0::keyRepeat(KeyID key, KeyModifierMask mask,
+ SInt32 count, KeyButton)
+{
+ 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);
+}
+
+void
+ClientProxy1_0::keyUp(KeyID key, KeyModifierMask mask, KeyButton)
+{
+ LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x", getName().c_str(), key, mask));
+ ProtocolUtil::writef(getStream(), kMsgDKeyUp1_0, key, mask);
+}
+
+void
+ClientProxy1_0::mouseDown(ButtonID button)
+{
+ LOG((CLOG_DEBUG1 "send mouse down to \"%s\" id=%d", getName().c_str(), button));
+ ProtocolUtil::writef(getStream(), kMsgDMouseDown, button);
+}
+
+void
+ClientProxy1_0::mouseUp(ButtonID button)
+{
+ LOG((CLOG_DEBUG1 "send mouse up to \"%s\" id=%d", getName().c_str(), button));
+ ProtocolUtil::writef(getStream(), kMsgDMouseUp, button);
+}
+
+void
+ClientProxy1_0::mouseMove(SInt32 xAbs, SInt32 yAbs)
+{
+ LOG((CLOG_DEBUG2 "send mouse move to \"%s\" %d,%d", getName().c_str(), xAbs, yAbs));
+ ProtocolUtil::writef(getStream(), kMsgDMouseMove, xAbs, yAbs);
+}
+
+void
+ClientProxy1_0::mouseRelativeMove(SInt32, SInt32)
+{
+ // ignore -- not supported in protocol 1.0
+}
+
+void
+ClientProxy1_0::mouseWheel(SInt32, SInt32 yDelta)
+{
+ // clients prior to 1.3 only support the y axis
+ LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d", getName().c_str(), yDelta));
+ ProtocolUtil::writef(getStream(), kMsgDMouseWheel1_0, yDelta);
+}
+
+void
+ClientProxy1_0::sendDragInfo(UInt32 fileCount, const char* info, size_t size)
+{
+ // ignore -- not supported in protocol 1.0
+ LOG((CLOG_DEBUG "draggingInfoSending not supported"));
+}
+
+void
+ClientProxy1_0::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
+{
+ // ignore -- not supported in protocol 1.0
+ LOG((CLOG_DEBUG "fileChunkSending not supported"));
+}
+
+void
+ClientProxy1_0::screensaver(bool on)
+{
+ LOG((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getName().c_str(), on ? 1 : 0));
+ ProtocolUtil::writef(getStream(), kMsgCScreenSaver, on ? 1 : 0);
+}
+
+void
+ClientProxy1_0::resetOptions()
+{
+ LOG((CLOG_DEBUG1 "send reset options to \"%s\"", getName().c_str()));
+ ProtocolUtil::writef(getStream(), kMsgCResetOptions);
+
+ // reset heart rate and death
+ resetHeartbeatRate();
+ removeHeartbeatTimer();
+ addHeartbeatTimer();
+}
+
+void
+ClientProxy1_0::setOptions(const OptionsList& options)
+{
+ LOG((CLOG_DEBUG1 "send set options to \"%s\" size=%d", getName().c_str(), options.size()));
+ ProtocolUtil::writef(getStream(), kMsgDSetOptions, &options);
+
+ // check options
+ for (UInt32 i = 0, n = (UInt32)options.size(); i < n; i += 2) {
+ if (options[i] == kOptionHeartbeat) {
+ double rate = 1.0e-3 * static_cast<double>(options[i + 1]);
+ if (rate <= 0.0) {
+ rate = -1.0;
+ }
+ setHeartbeatRate(rate, rate * kHeartBeatsUntilDeath);
+ removeHeartbeatTimer();
+ addHeartbeatTimer();
+ }
+ }
+}
+
+bool
+ClientProxy1_0::recvInfo()
+{
+ // parse the message
+ SInt16 x, y, w, h, dummy1, mx, my;
+ if (!ProtocolUtil::readf(getStream(), kMsgDInfo + 4,
+ &x, &y, &w, &h, &dummy1, &mx, &my)) {
+ return false;
+ }
+ LOG((CLOG_DEBUG "received client \"%s\" info shape=%d,%d %dx%d at %d,%d", getName().c_str(), x, y, w, h, mx, my));
+
+ // validate
+ if (w <= 0 || h <= 0) {
+ return false;
+ }
+ if (mx < x || mx >= x + w || my < y || my >= y + h) {
+ mx = x + w / 2;
+ my = y + h / 2;
+ }
+
+ // save
+ m_info.m_x = x;
+ m_info.m_y = y;
+ m_info.m_w = w;
+ m_info.m_h = h;
+ m_info.m_mx = mx;
+ m_info.m_my = my;
+
+ // acknowledge receipt
+ LOG((CLOG_DEBUG1 "send info ack to \"%s\"", getName().c_str()));
+ ProtocolUtil::writef(getStream(), kMsgCInfoAck);
+ return true;
+}
+
+bool
+ClientProxy1_0::recvClipboard()
+{
+ // deprecated in protocol 1.0
+ return false;
+}
+
+bool
+ClientProxy1_0::recvGrabClipboard()
+{
+ // parse message
+ ClipboardID id;
+ UInt32 seqNum;
+ if (!ProtocolUtil::readf(getStream(), kMsgCClipboard + 4, &id, &seqNum)) {
+ return false;
+ }
+ LOG((CLOG_DEBUG "received client \"%s\" grabbed clipboard %d seqnum=%d", getName().c_str(), id, seqNum));
+
+ // validate
+ if (id >= kClipboardEnd) {
+ return false;
+ }
+
+ // notify
+ ClipboardInfo* info = new ClipboardInfo;
+ info->m_id = id;
+ info->m_sequenceNumber = seqNum;
+ m_events->addEvent(Event(m_events->forClipboard().clipboardGrabbed(),
+ getEventTarget(), info));
+
+ return true;
+}
+
+//
+// ClientProxy1_0::ClientClipboard
+//
+
+ClientProxy1_0::ClientClipboard::ClientClipboard() :
+ m_clipboard(),
+ m_sequenceNumber(0),
+ m_dirty(true)
+{
+ // do nothing
+}
diff --git a/src/lib/server/ClientProxy1_0.h b/src/lib/server/ClientProxy1_0.h
new file mode 100644
index 0000000..0720232
--- /dev/null
+++ b/src/lib/server/ClientProxy1_0.h
@@ -0,0 +1,107 @@
+/*
+ * barrier -- 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 "server/ClientProxy.h"
+#include "barrier/Clipboard.h"
+#include "barrier/protocol_types.h"
+
+class Event;
+class EventQueueTimer;
+class IEventQueue;
+
+//! Proxy for client implementing protocol version 1.0
+class ClientProxy1_0 : public ClientProxy {
+public:
+ ClientProxy1_0(const String& name, barrier::IStream* adoptedStream, IEventQueue* events);
+ ~ClientProxy1_0();
+
+ // IScreen
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const;
+
+ // IClient overrides
+ virtual void enter(SInt32 xAbs, SInt32 yAbs,
+ UInt32 seqNum, KeyModifierMask mask,
+ bool forScreensaver);
+ virtual bool leave();
+ virtual void setClipboard(ClipboardID, const IClipboard*);
+ virtual void grabClipboard(ClipboardID);
+ virtual void setClipboardDirty(ClipboardID, bool);
+ virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
+ virtual void keyRepeat(KeyID, KeyModifierMask,
+ SInt32 count, KeyButton);
+ virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
+ virtual void mouseDown(ButtonID);
+ virtual void mouseUp(ButtonID);
+ virtual void mouseMove(SInt32 xAbs, SInt32 yAbs);
+ virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel);
+ virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta);
+ virtual void screensaver(bool activate);
+ virtual void resetOptions();
+ virtual void setOptions(const OptionsList& options);
+ virtual void sendDragInfo(UInt32 fileCount, const char* info, size_t size);
+ virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize);
+
+protected:
+ virtual bool parseHandshakeMessage(const UInt8* code);
+ virtual bool parseMessage(const UInt8* code);
+
+ virtual void resetHeartbeatRate();
+ virtual void setHeartbeatRate(double rate, double alarm);
+ virtual void resetHeartbeatTimer();
+ virtual void addHeartbeatTimer();
+ virtual void removeHeartbeatTimer();
+ virtual bool recvClipboard();
+private:
+ void disconnect();
+ void removeHandlers();
+
+ void handleData(const Event&, void*);
+ void handleDisconnect(const Event&, void*);
+ void handleWriteError(const Event&, void*);
+ void handleFlatline(const Event&, void*);
+
+ bool recvInfo();
+ bool recvGrabClipboard();
+
+protected:
+ struct ClientClipboard {
+ public:
+ ClientClipboard();
+
+ public:
+ Clipboard m_clipboard;
+ UInt32 m_sequenceNumber;
+ bool m_dirty;
+ };
+
+ ClientClipboard m_clipboard[kClipboardEnd];
+
+private:
+ typedef bool (ClientProxy1_0::*MessageParser)(const UInt8*);
+
+ ClientInfo m_info;
+ double m_heartbeatAlarm;
+ EventQueueTimer* m_heartbeatTimer;
+ MessageParser m_parser;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/server/ClientProxy1_1.cpp b/src/lib/server/ClientProxy1_1.cpp
new file mode 100644
index 0000000..b7eb4c4
--- /dev/null
+++ b/src/lib/server/ClientProxy1_1.cpp
@@ -0,0 +1,61 @@
+/*
+ * barrier -- 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 "server/ClientProxy1_1.h"
+
+#include "barrier/ProtocolUtil.h"
+#include "base/Log.h"
+
+#include <cstring>
+
+//
+// ClientProxy1_1
+//
+
+ClientProxy1_1::ClientProxy1_1(const String& name, barrier::IStream* stream, IEventQueue* events) :
+ ClientProxy1_0(name, stream, events)
+{
+ // do nothing
+}
+
+ClientProxy1_1::~ClientProxy1_1()
+{
+ // do nothing
+}
+
+void
+ClientProxy1_1::keyDown(KeyID key, KeyModifierMask mask, KeyButton button)
+{
+ 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);
+}
+
+void
+ClientProxy1_1::keyRepeat(KeyID key, KeyModifierMask mask,
+ SInt32 count, KeyButton button)
+{
+ 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);
+}
+
+void
+ClientProxy1_1::keyUp(KeyID key, KeyModifierMask mask, KeyButton button)
+{
+ LOG((CLOG_DEBUG1 "send key up to \"%s\" id=%d, mask=0x%04x, button=0x%04x", getName().c_str(), key, mask, button));
+ ProtocolUtil::writef(getStream(), kMsgDKeyUp, key, mask, button);
+}
diff --git a/src/lib/server/ClientProxy1_1.h b/src/lib/server/ClientProxy1_1.h
new file mode 100644
index 0000000..cdb674d
--- /dev/null
+++ b/src/lib/server/ClientProxy1_1.h
@@ -0,0 +1,34 @@
+/*
+ * barrier -- 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 "server/ClientProxy1_0.h"
+
+//! Proxy for client implementing protocol version 1.1
+class ClientProxy1_1 : public ClientProxy1_0 {
+public:
+ ClientProxy1_1(const String& name, barrier::IStream* adoptedStream, IEventQueue* events);
+ ~ClientProxy1_1();
+
+ // IClient overrides
+ virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
+ virtual void keyRepeat(KeyID, KeyModifierMask,
+ SInt32 count, KeyButton);
+ virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
+};
diff --git a/src/lib/server/ClientProxy1_2.cpp b/src/lib/server/ClientProxy1_2.cpp
new file mode 100644
index 0000000..2dd13cf
--- /dev/null
+++ b/src/lib/server/ClientProxy1_2.cpp
@@ -0,0 +1,44 @@
+/*
+ * barrier -- 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 "server/ClientProxy1_2.h"
+
+#include "barrier/ProtocolUtil.h"
+#include "base/Log.h"
+
+//
+// ClientProxy1_1
+//
+
+ClientProxy1_2::ClientProxy1_2(const String& name, barrier::IStream* stream, IEventQueue* events) :
+ ClientProxy1_1(name, stream, events)
+{
+ // do nothing
+}
+
+ClientProxy1_2::~ClientProxy1_2()
+{
+ // do nothing
+}
+
+void
+ClientProxy1_2::mouseRelativeMove(SInt32 xRel, SInt32 yRel)
+{
+ LOG((CLOG_DEBUG2 "send mouse relative move to \"%s\" %d,%d", getName().c_str(), xRel, yRel));
+ ProtocolUtil::writef(getStream(), kMsgDMouseRelMove, xRel, yRel);
+}
diff --git a/src/lib/server/ClientProxy1_2.h b/src/lib/server/ClientProxy1_2.h
new file mode 100644
index 0000000..f6ffe94
--- /dev/null
+++ b/src/lib/server/ClientProxy1_2.h
@@ -0,0 +1,33 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "server/ClientProxy1_1.h"
+
+class IEventQueue;
+
+//! Proxy for client implementing protocol version 1.2
+class ClientProxy1_2 : public ClientProxy1_1 {
+public:
+ ClientProxy1_2(const String& name, barrier::IStream* adoptedStream, IEventQueue* events);
+ ~ClientProxy1_2();
+
+ // IClient overrides
+ virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel);
+};
diff --git a/src/lib/server/ClientProxy1_3.cpp b/src/lib/server/ClientProxy1_3.cpp
new file mode 100644
index 0000000..34ea0c8
--- /dev/null
+++ b/src/lib/server/ClientProxy1_3.cpp
@@ -0,0 +1,129 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2006 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 "server/ClientProxy1_3.h"
+
+#include "barrier/ProtocolUtil.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+
+#include <cstring>
+#include <memory>
+
+//
+// ClientProxy1_3
+//
+
+ClientProxy1_3::ClientProxy1_3(const String& name, barrier::IStream* stream, IEventQueue* events) :
+ ClientProxy1_2(name, stream, events),
+ m_keepAliveRate(kKeepAliveRate),
+ m_keepAliveTimer(NULL),
+ m_events(events)
+{
+ setHeartbeatRate(kKeepAliveRate, kKeepAliveRate * kKeepAlivesUntilDeath);
+}
+
+ClientProxy1_3::~ClientProxy1_3()
+{
+ // cannot do this in superclass or our override wouldn't get called
+ removeHeartbeatTimer();
+}
+
+void
+ClientProxy1_3::mouseWheel(SInt32 xDelta, SInt32 yDelta)
+{
+ LOG((CLOG_DEBUG2 "send mouse wheel to \"%s\" %+d,%+d", getName().c_str(), xDelta, yDelta));
+ ProtocolUtil::writef(getStream(), kMsgDMouseWheel, xDelta, yDelta);
+}
+
+bool
+ClientProxy1_3::parseMessage(const UInt8* code)
+{
+ // process message
+ if (memcmp(code, kMsgCKeepAlive, 4) == 0) {
+ // reset alarm
+ resetHeartbeatTimer();
+ return true;
+ }
+ else {
+ return ClientProxy1_2::parseMessage(code);
+ }
+}
+
+void
+ClientProxy1_3::resetHeartbeatRate()
+{
+ setHeartbeatRate(kKeepAliveRate, kKeepAliveRate * kKeepAlivesUntilDeath);
+}
+
+void
+ClientProxy1_3::setHeartbeatRate(double rate, double)
+{
+ m_keepAliveRate = rate;
+ ClientProxy1_2::setHeartbeatRate(rate, rate * kKeepAlivesUntilDeath);
+}
+
+void
+ClientProxy1_3::resetHeartbeatTimer()
+{
+ // reset the alarm but not the keep alive timer
+ ClientProxy1_2::removeHeartbeatTimer();
+ ClientProxy1_2::addHeartbeatTimer();
+}
+
+void
+ClientProxy1_3::addHeartbeatTimer()
+{
+ // create and install a timer to periodically send keep alives
+ if (m_keepAliveRate > 0.0) {
+ m_keepAliveTimer = m_events->newTimer(m_keepAliveRate, NULL);
+ m_events->adoptHandler(Event::kTimer, m_keepAliveTimer,
+ new TMethodEventJob<ClientProxy1_3>(this,
+ &ClientProxy1_3::handleKeepAlive, NULL));
+ }
+
+ // superclass does the alarm
+ ClientProxy1_2::addHeartbeatTimer();
+}
+
+void
+ClientProxy1_3::removeHeartbeatTimer()
+{
+ // remove the timer that sends keep alives periodically
+ if (m_keepAliveTimer != NULL) {
+ m_events->removeHandler(Event::kTimer, m_keepAliveTimer);
+ m_events->deleteTimer(m_keepAliveTimer);
+ m_keepAliveTimer = NULL;
+ }
+
+ // superclass does the alarm
+ ClientProxy1_2::removeHeartbeatTimer();
+}
+
+void
+ClientProxy1_3::handleKeepAlive(const Event&, void*)
+{
+ keepAlive();
+}
+
+void
+ClientProxy1_3::keepAlive()
+{
+ ProtocolUtil::writef(getStream(), kMsgCKeepAlive);
+}
diff --git a/src/lib/server/ClientProxy1_3.h b/src/lib/server/ClientProxy1_3.h
new file mode 100644
index 0000000..ff2ed0a
--- /dev/null
+++ b/src/lib/server/ClientProxy1_3.h
@@ -0,0 +1,48 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2006 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 "server/ClientProxy1_2.h"
+
+//! Proxy for client implementing protocol version 1.3
+class ClientProxy1_3 : public ClientProxy1_2 {
+public:
+ ClientProxy1_3(const String& name, barrier::IStream* adoptedStream, IEventQueue* events);
+ ~ClientProxy1_3();
+
+ // IClient overrides
+ virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta);
+
+ void handleKeepAlive(const Event&, void*);
+
+protected:
+ // ClientProxy overrides
+ virtual bool parseMessage(const UInt8* code);
+ virtual void resetHeartbeatRate();
+ virtual void setHeartbeatRate(double rate, double alarm);
+ virtual void resetHeartbeatTimer();
+ virtual void addHeartbeatTimer();
+ virtual void removeHeartbeatTimer();
+ virtual void keepAlive();
+
+private:
+ double m_keepAliveRate;
+ EventQueueTimer* m_keepAliveTimer;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/server/ClientProxy1_4.cpp b/src/lib/server/ClientProxy1_4.cpp
new file mode 100644
index 0000000..43c708d
--- /dev/null
+++ b/src/lib/server/ClientProxy1_4.cpp
@@ -0,0 +1,66 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 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 "server/ClientProxy1_4.h"
+
+#include "server/Server.h"
+#include "barrier/ProtocolUtil.h"
+#include "base/Log.h"
+#include "base/IEventQueue.h"
+#include "base/TMethodEventJob.h"
+
+#include <cstring>
+#include <memory>
+
+//
+// ClientProxy1_4
+//
+
+ClientProxy1_4::ClientProxy1_4(const String& name, barrier::IStream* stream, Server* server, IEventQueue* events) :
+ ClientProxy1_3(name, stream, events), m_server(server)
+{
+ assert(m_server != NULL);
+}
+
+ClientProxy1_4::~ClientProxy1_4()
+{
+}
+
+void
+ClientProxy1_4::keyDown(KeyID key, KeyModifierMask mask, KeyButton button)
+{
+ ClientProxy1_3::keyDown(key, mask, button);
+}
+
+void
+ClientProxy1_4::keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button)
+{
+ ClientProxy1_3::keyRepeat(key, mask, count, button);
+}
+
+void
+ClientProxy1_4::keyUp(KeyID key, KeyModifierMask mask, KeyButton button)
+{
+ ClientProxy1_3::keyUp(key, mask, button);
+}
+
+void
+ClientProxy1_4::keepAlive()
+{
+ ClientProxy1_3::keepAlive();
+}
diff --git a/src/lib/server/ClientProxy1_4.h b/src/lib/server/ClientProxy1_4.h
new file mode 100644
index 0000000..6c55965
--- /dev/null
+++ b/src/lib/server/ClientProxy1_4.h
@@ -0,0 +1,46 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 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 "server/ClientProxy1_3.h"
+
+class Server;
+
+//! Proxy for client implementing protocol version 1.4
+class ClientProxy1_4 : public ClientProxy1_3 {
+public:
+ ClientProxy1_4(const String& name, barrier::IStream* adoptedStream, Server* server, IEventQueue* events);
+ ~ClientProxy1_4();
+
+ //! @name accessors
+ //@{
+
+ //! get server pointer
+ Server* getServer() { return m_server; }
+
+ //@}
+
+ // IClient overrides
+ virtual void keyDown(KeyID key, KeyModifierMask mask, KeyButton button);
+ virtual void keyRepeat(KeyID key, KeyModifierMask mask, SInt32 count, KeyButton button);
+ virtual void keyUp(KeyID key, KeyModifierMask mask, KeyButton button);
+ virtual void keepAlive();
+
+ Server* m_server;
+};
diff --git a/src/lib/server/ClientProxy1_5.cpp b/src/lib/server/ClientProxy1_5.cpp
new file mode 100644
index 0000000..43fd0b7
--- /dev/null
+++ b/src/lib/server/ClientProxy1_5.cpp
@@ -0,0 +1,110 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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 "server/ClientProxy1_5.h"
+
+#include "server/Server.h"
+#include "barrier/FileChunk.h"
+#include "barrier/StreamChunker.h"
+#include "barrier/ProtocolUtil.h"
+#include "io/IStream.h"
+#include "base/TMethodEventJob.h"
+#include "base/Log.h"
+
+#include <sstream>
+
+//
+// ClientProxy1_5
+//
+
+ClientProxy1_5::ClientProxy1_5(const String& name, barrier::IStream* stream, Server* server, IEventQueue* events) :
+ ClientProxy1_4(name, stream, server, events),
+ m_events(events)
+{
+
+ m_events->adoptHandler(m_events->forFile().keepAlive(),
+ this,
+ new TMethodEventJob<ClientProxy1_3>(this,
+ &ClientProxy1_3::handleKeepAlive, NULL));
+}
+
+ClientProxy1_5::~ClientProxy1_5()
+{
+ m_events->removeHandler(m_events->forFile().keepAlive(), this);
+}
+
+void
+ClientProxy1_5::sendDragInfo(UInt32 fileCount, const char* info, size_t size)
+{
+ String data(info, size);
+
+ ProtocolUtil::writef(getStream(), kMsgDDragInfo, fileCount, &data);
+}
+
+void
+ClientProxy1_5::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
+{
+ FileChunk::send(getStream(), mark, data, dataSize);
+}
+
+bool
+ClientProxy1_5::parseMessage(const UInt8* code)
+{
+ if (memcmp(code, kMsgDFileTransfer, 4) == 0) {
+ fileChunkReceived();
+ }
+ else if (memcmp(code, kMsgDDragInfo, 4) == 0) {
+ dragInfoReceived();
+ }
+ else {
+ return ClientProxy1_4::parseMessage(code);
+ }
+
+ return true;
+}
+
+void
+ClientProxy1_5::fileChunkReceived()
+{
+ Server* server = getServer();
+ int result = FileChunk::assemble(
+ getStream(),
+ server->getReceivedFileData(),
+ server->getExpectedFileSize());
+
+
+ if (result == kFinish) {
+ m_events->addEvent(Event(m_events->forFile().fileRecieveCompleted(), server));
+ }
+ else if (result == kStart) {
+ if (server->getFakeDragFileList().size() > 0) {
+ String filename = server->getFakeDragFileList().at(0).getFilename();
+ LOG((CLOG_DEBUG "start receiving %s", filename.c_str()));
+ }
+ }
+}
+
+void
+ClientProxy1_5::dragInfoReceived()
+{
+ // parse
+ UInt32 fileNum = 0;
+ String content;
+ ProtocolUtil::readf(getStream(), kMsgDDragInfo + 4, &fileNum, &content);
+
+ m_server->dragInfoReceived(fileNum, content);
+}
diff --git a/src/lib/server/ClientProxy1_5.h b/src/lib/server/ClientProxy1_5.h
new file mode 100644
index 0000000..776de03
--- /dev/null
+++ b/src/lib/server/ClientProxy1_5.h
@@ -0,0 +1,41 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#include "server/ClientProxy1_4.h"
+#include "base/Stopwatch.h"
+#include "common/stdvector.h"
+
+class Server;
+class IEventQueue;
+
+//! Proxy for client implementing protocol version 1.5
+class ClientProxy1_5 : public ClientProxy1_4 {
+public:
+ ClientProxy1_5(const String& name, barrier::IStream* adoptedStream, Server* server, IEventQueue* events);
+ ~ClientProxy1_5();
+
+ virtual void sendDragInfo(UInt32 fileCount, const char* info, size_t size);
+ virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize);
+ virtual bool parseMessage(const UInt8* code);
+ void fileChunkReceived();
+ void dragInfoReceived();
+
+private:
+ IEventQueue* m_events;
+};
diff --git a/src/lib/server/ClientProxy1_6.cpp b/src/lib/server/ClientProxy1_6.cpp
new file mode 100644
index 0000000..a0d2621
--- /dev/null
+++ b/src/lib/server/ClientProxy1_6.cpp
@@ -0,0 +1,100 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "server/ClientProxy1_6.h"
+
+#include "server/Server.h"
+#include "barrier/ProtocolUtil.h"
+#include "barrier/StreamChunker.h"
+#include "barrier/ClipboardChunk.h"
+#include "io/IStream.h"
+#include "base/TMethodEventJob.h"
+#include "base/Log.h"
+
+//
+// ClientProxy1_6
+//
+
+ClientProxy1_6::ClientProxy1_6(const String& name, barrier::IStream* stream, Server* server, IEventQueue* events) :
+ ClientProxy1_5(name, stream, server, events),
+ m_events(events)
+{
+ m_events->adoptHandler(m_events->forClipboard().clipboardSending(),
+ this,
+ new TMethodEventJob<ClientProxy1_6>(this,
+ &ClientProxy1_6::handleClipboardSendingEvent));
+}
+
+ClientProxy1_6::~ClientProxy1_6()
+{
+}
+
+void
+ClientProxy1_6::setClipboard(ClipboardID id, const IClipboard* clipboard)
+{
+ // ignore if this clipboard is already clean
+ if (m_clipboard[id].m_dirty) {
+ // this clipboard is now clean
+ m_clipboard[id].m_dirty = false;
+ Clipboard::copy(&m_clipboard[id].m_clipboard, clipboard);
+
+ String data = m_clipboard[id].m_clipboard.marshall();
+
+ size_t size = data.size();
+ LOG((CLOG_DEBUG "sending clipboard %d to \"%s\"", id, getName().c_str()));
+
+ StreamChunker::sendClipboard(data, size, id, 0, m_events, this);
+ }
+}
+
+void
+ClientProxy1_6::handleClipboardSendingEvent(const Event& event, void*)
+{
+ ClipboardChunk::send(getStream(), event.getData());
+}
+
+bool
+ClientProxy1_6::recvClipboard()
+{
+ // parse message
+ static String dataCached;
+ ClipboardID id;
+ UInt32 seq;
+
+ int r = ClipboardChunk::assemble(getStream(), dataCached, id, seq);
+
+ if (r == kStart) {
+ size_t size = ClipboardChunk::getExpectedSize();
+ LOG((CLOG_DEBUG "receiving clipboard %d size=%d", id, size));
+ }
+ else if (r == kFinish) {
+ LOG((CLOG_DEBUG "received client \"%s\" clipboard %d seqnum=%d, size=%d",
+ getName().c_str(), id, seq, dataCached.size()));
+ // save clipboard
+ m_clipboard[id].m_clipboard.unmarshall(dataCached, 0);
+ m_clipboard[id].m_sequenceNumber = seq;
+
+ // notify
+ ClipboardInfo* info = new ClipboardInfo;
+ info->m_id = id;
+ info->m_sequenceNumber = seq;
+ m_events->addEvent(Event(m_events->forClipboard().clipboardChanged(),
+ getEventTarget(), info));
+ }
+
+ return true;
+}
diff --git a/src/lib/server/ClientProxy1_6.h b/src/lib/server/ClientProxy1_6.h
new file mode 100644
index 0000000..838cb02
--- /dev/null
+++ b/src/lib/server/ClientProxy1_6.h
@@ -0,0 +1,39 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+#include "server/ClientProxy1_5.h"
+
+class Server;
+class IEventQueue;
+
+//! Proxy for client implementing protocol version 1.6
+class ClientProxy1_6 : public ClientProxy1_5 {
+public:
+ ClientProxy1_6(const String& name, barrier::IStream* adoptedStream, Server* server, IEventQueue* events);
+ ~ClientProxy1_6();
+
+ virtual void setClipboard(ClipboardID id, const IClipboard* clipboard);
+ virtual bool recvClipboard();
+
+private:
+ void handleClipboardSendingEvent(const Event&, void*);
+
+private:
+ IEventQueue* m_events;
+};
diff --git a/src/lib/server/ClientProxyUnknown.cpp b/src/lib/server/ClientProxyUnknown.cpp
new file mode 100644
index 0000000..f929108
--- /dev/null
+++ b/src/lib/server/ClientProxyUnknown.cpp
@@ -0,0 +1,295 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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 "server/ClientProxyUnknown.h"
+
+#include "server/Server.h"
+#include "server/ClientProxy1_0.h"
+#include "server/ClientProxy1_1.h"
+#include "server/ClientProxy1_2.h"
+#include "server/ClientProxy1_3.h"
+#include "server/ClientProxy1_4.h"
+#include "server/ClientProxy1_5.h"
+#include "server/ClientProxy1_6.h"
+#include "barrier/protocol_types.h"
+#include "barrier/ProtocolUtil.h"
+#include "barrier/XBarrier.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"
+
+//
+// ClientProxyUnknown
+//
+
+ClientProxyUnknown::ClientProxyUnknown(barrier::IStream* stream, double timeout, Server* server, IEventQueue* events) :
+ m_stream(stream),
+ m_proxy(NULL),
+ m_ready(false),
+ m_server(server),
+ m_events(events)
+{
+ assert(m_server != NULL);
+
+ m_events->adoptHandler(Event::kTimer, this,
+ new TMethodEventJob<ClientProxyUnknown>(this,
+ &ClientProxyUnknown::handleTimeout, NULL));
+ m_timer = m_events->newOneShotTimer(timeout, this);
+ addStreamHandlers();
+
+ LOG((CLOG_DEBUG1 "saying hello"));
+ ProtocolUtil::writef(m_stream, kMsgHello,
+ kProtocolMajorVersion,
+ kProtocolMinorVersion);
+}
+
+ClientProxyUnknown::~ClientProxyUnknown()
+{
+ removeHandlers();
+ removeTimer();
+ delete m_stream;
+ delete m_proxy;
+}
+
+ClientProxy*
+ClientProxyUnknown::orphanClientProxy()
+{
+ if (m_ready) {
+ removeHandlers();
+ ClientProxy* proxy = m_proxy;
+ m_proxy = NULL;
+ return proxy;
+ }
+ else {
+ return NULL;
+ }
+}
+
+void
+ClientProxyUnknown::sendSuccess()
+{
+ m_ready = true;
+ removeTimer();
+ m_events->addEvent(Event(m_events->forClientProxyUnknown().success(), this));
+}
+
+void
+ClientProxyUnknown::sendFailure()
+{
+ delete m_proxy;
+ m_proxy = NULL;
+ m_ready = false;
+ removeHandlers();
+ removeTimer();
+ m_events->addEvent(Event(m_events->forClientProxyUnknown().failure(), this));
+}
+
+void
+ClientProxyUnknown::addStreamHandlers()
+{
+ assert(m_stream != NULL);
+
+ m_events->adoptHandler(m_events->forIStream().inputReady(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<ClientProxyUnknown>(this,
+ &ClientProxyUnknown::handleData));
+ m_events->adoptHandler(m_events->forIStream().outputError(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<ClientProxyUnknown>(this,
+ &ClientProxyUnknown::handleWriteError));
+ m_events->adoptHandler(m_events->forIStream().inputShutdown(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<ClientProxyUnknown>(this,
+ &ClientProxyUnknown::handleDisconnect));
+ m_events->adoptHandler(m_events->forIStream().outputShutdown(),
+ m_stream->getEventTarget(),
+ new TMethodEventJob<ClientProxyUnknown>(this,
+ &ClientProxyUnknown::handleWriteError));
+}
+
+void
+ClientProxyUnknown::addProxyHandlers()
+{
+ assert(m_proxy != NULL);
+
+ m_events->adoptHandler(m_events->forClientProxy().ready(),
+ m_proxy,
+ new TMethodEventJob<ClientProxyUnknown>(this,
+ &ClientProxyUnknown::handleReady));
+ m_events->adoptHandler(m_events->forClientProxy().disconnected(),
+ m_proxy,
+ new TMethodEventJob<ClientProxyUnknown>(this,
+ &ClientProxyUnknown::handleDisconnect));
+}
+
+void
+ClientProxyUnknown::removeHandlers()
+{
+ if (m_stream != NULL) {
+ m_events->removeHandler(m_events->forIStream().inputReady(),
+ m_stream->getEventTarget());
+ m_events->removeHandler(m_events->forIStream().outputError(),
+ m_stream->getEventTarget());
+ m_events->removeHandler(m_events->forIStream().inputShutdown(),
+ m_stream->getEventTarget());
+ m_events->removeHandler(m_events->forIStream().outputShutdown(),
+ m_stream->getEventTarget());
+ }
+ if (m_proxy != NULL) {
+ m_events->removeHandler(m_events->forClientProxy().ready(),
+ m_proxy);
+ m_events->removeHandler(m_events->forClientProxy().disconnected(),
+ m_proxy);
+ }
+}
+
+void
+ClientProxyUnknown::removeTimer()
+{
+ if (m_timer != NULL) {
+ m_events->deleteTimer(m_timer);
+ m_events->removeHandler(Event::kTimer, this);
+ m_timer = NULL;
+ }
+}
+
+void
+ClientProxyUnknown::handleData(const Event&, void*)
+{
+ LOG((CLOG_DEBUG1 "parsing hello reply"));
+
+ String name("<unknown>");
+ try {
+ // limit the maximum length of the hello
+ UInt32 n = m_stream->getSize();
+ if (n > kMaxHelloLength) {
+ LOG((CLOG_DEBUG1 "hello reply too long"));
+ throw XBadClient();
+ }
+
+ // parse the reply to hello
+ SInt16 major, minor;
+ if (!ProtocolUtil::readf(m_stream, kMsgHelloBack,
+ &major, &minor, &name)) {
+ throw XBadClient();
+ }
+
+ // disallow invalid version numbers
+ if (major <= 0 || minor < 0) {
+ throw XIncompatibleClient(major, minor);
+ }
+
+ // remove stream event handlers. the proxy we're about to create
+ // may install its own handlers and we don't want to accidentally
+ // remove those later.
+ removeHandlers();
+
+ // create client proxy for highest version supported by the client
+ if (major == 1) {
+ switch (minor) {
+ case 0:
+ m_proxy = new ClientProxy1_0(name, m_stream, m_events);
+ break;
+
+ case 1:
+ m_proxy = new ClientProxy1_1(name, m_stream, m_events);
+ break;
+
+ case 2:
+ m_proxy = new ClientProxy1_2(name, m_stream, m_events);
+ break;
+
+ case 3:
+ m_proxy = new ClientProxy1_3(name, m_stream, m_events);
+ break;
+
+ case 4:
+ m_proxy = new ClientProxy1_4(name, m_stream, m_server, m_events);
+ break;
+
+ case 5:
+ m_proxy = new ClientProxy1_5(name, m_stream, m_server, m_events);
+ break;
+
+ case 6:
+ m_proxy = new ClientProxy1_6(name, m_stream, m_server, m_events);
+ break;
+ }
+ }
+
+ // hangup (with error) if version isn't supported
+ if (m_proxy == NULL) {
+ throw XIncompatibleClient(major, minor);
+ }
+
+ // 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;
+
+ // wait until the proxy signals that it's ready or has disconnected
+ addProxyHandlers();
+ return;
+ }
+ catch (XIncompatibleClient& e) {
+ // client is incompatible
+ LOG((CLOG_WARN "client \"%s\" has incompatible version %d.%d)", name.c_str(), e.getMajor(), e.getMinor()));
+ ProtocolUtil::writef(m_stream,
+ kMsgEIncompatible,
+ kProtocolMajorVersion, kProtocolMinorVersion);
+ }
+ catch (XBadClient&) {
+ // client not behaving
+ LOG((CLOG_WARN "protocol error from client \"%s\"", name.c_str()));
+ ProtocolUtil::writef(m_stream, kMsgEBad);
+ }
+ catch (XBase& e) {
+ // misc error
+ LOG((CLOG_WARN "error communicating with client \"%s\": %s", name.c_str(), e.what()));
+ }
+ sendFailure();
+}
+
+void
+ClientProxyUnknown::handleWriteError(const Event&, void*)
+{
+ LOG((CLOG_NOTE "error communicating with new client"));
+ sendFailure();
+}
+
+void
+ClientProxyUnknown::handleTimeout(const Event&, void*)
+{
+ LOG((CLOG_NOTE "new client is unresponsive"));
+ sendFailure();
+}
+
+void
+ClientProxyUnknown::handleDisconnect(const Event&, void*)
+{
+ LOG((CLOG_NOTE "new client disconnected"));
+ sendFailure();
+}
+
+void
+ClientProxyUnknown::handleReady(const Event&, void*)
+{
+ sendSuccess();
+}
diff --git a/src/lib/server/ClientProxyUnknown.h b/src/lib/server/ClientProxyUnknown.h
new file mode 100644
index 0000000..5d59402
--- /dev/null
+++ b/src/lib/server/ClientProxyUnknown.h
@@ -0,0 +1,71 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2004 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/Event.h"
+#include "base/EventTypes.h"
+
+class ClientProxy;
+class EventQueueTimer;
+namespace barrier { class IStream; }
+class Server;
+class IEventQueue;
+
+class ClientProxyUnknown {
+public:
+ ClientProxyUnknown(barrier::IStream* stream, double timeout, Server* server, IEventQueue* events);
+ ~ClientProxyUnknown();
+
+ //! @name manipulators
+ //@{
+
+ //! Get the client proxy
+ /*!
+ Returns the client proxy created after a successful handshake
+ (i.e. when this object sends a success event). Returns NULL
+ if the handshake is unsuccessful or incomplete.
+ */
+ ClientProxy* orphanClientProxy();
+
+ //! Get the stream
+ barrier::IStream* getStream() { return m_stream; }
+
+ //@}
+
+private:
+ void sendSuccess();
+ void sendFailure();
+ void addStreamHandlers();
+ void addProxyHandlers();
+ void removeHandlers();
+ void removeTimer();
+ void handleData(const Event&, void*);
+ void handleWriteError(const Event&, void*);
+ void handleTimeout(const Event&, void*);
+ void handleDisconnect(const Event&, void*);
+ void handleReady(const Event&, void*);
+
+private:
+ barrier::IStream* m_stream;
+ EventQueueTimer* m_timer;
+ ClientProxy* m_proxy;
+ bool m_ready;
+ Server* m_server;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/server/Config.cpp b/src/lib/server/Config.cpp
new file mode 100644
index 0000000..3cf60a5
--- /dev/null
+++ b/src/lib/server/Config.cpp
@@ -0,0 +1,2335 @@
+/*
+ * barrier -- 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 "server/Config.h"
+
+#include "server/Server.h"
+#include "barrier/KeyMap.h"
+#include "barrier/key_types.h"
+#include "net/XSocket.h"
+#include "base/IEventQueue.h"
+#include "common/stdistream.h"
+#include "common/stdostream.h"
+
+#include <cstdlib>
+
+using namespace barrier::string;
+
+//
+// Config
+//
+
+Config::Config(IEventQueue* events) :
+ m_inputFilter(events),
+ m_hasLockToScreenAction(false),
+ m_events(events)
+{
+ // do nothing
+}
+
+Config::~Config()
+{
+ // do nothing
+}
+
+bool
+Config::addScreen(const String& name)
+{
+ // alias name must not exist
+ if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) {
+ return false;
+ }
+
+ // add cell
+ m_map.insert(std::make_pair(name, Cell()));
+
+ // add name
+ m_nameToCanonicalName.insert(std::make_pair(name, name));
+
+ return true;
+}
+
+bool
+Config::renameScreen(const String& oldName,
+ const String& newName)
+{
+ // get canonical name and find cell
+ String oldCanonical = getCanonicalName(oldName);
+ CellMap::iterator index = m_map.find(oldCanonical);
+ if (index == m_map.end()) {
+ return false;
+ }
+
+ // accept if names are equal but replace with new name to maintain
+ // case. otherwise, the new name must not exist.
+ if (!CaselessCmp::equal(oldName, newName) &&
+ m_nameToCanonicalName.find(newName) != m_nameToCanonicalName.end()) {
+ return false;
+ }
+
+ // update cell
+ Cell tmpCell = index->second;
+ m_map.erase(index);
+ m_map.insert(std::make_pair(newName, tmpCell));
+
+ // update name
+ m_nameToCanonicalName.erase(oldCanonical);
+ m_nameToCanonicalName.insert(std::make_pair(newName, newName));
+
+ // update connections
+ Name oldNameObj(this, oldName);
+ for (index = m_map.begin(); index != m_map.end(); ++index) {
+ index->second.rename(oldNameObj, newName);
+ }
+
+ // update alias targets
+ if (CaselessCmp::equal(oldName, oldCanonical)) {
+ for (NameMap::iterator iter = m_nameToCanonicalName.begin();
+ iter != m_nameToCanonicalName.end(); ++iter) {
+ if (CaselessCmp::equal(
+ iter->second, oldCanonical)) {
+ iter->second = newName;
+ }
+ }
+ }
+
+ return true;
+}
+
+void
+Config::removeScreen(const String& name)
+{
+ // get canonical name and find cell
+ String canonical = getCanonicalName(name);
+ CellMap::iterator index = m_map.find(canonical);
+ if (index == m_map.end()) {
+ return;
+ }
+
+ // remove from map
+ m_map.erase(index);
+
+ // disconnect
+ Name nameObj(this, name);
+ for (index = m_map.begin(); index != m_map.end(); ++index) {
+ index->second.remove(nameObj);
+ }
+
+ // remove aliases (and canonical name)
+ for (NameMap::iterator iter = m_nameToCanonicalName.begin();
+ iter != m_nameToCanonicalName.end(); ) {
+ if (iter->second == canonical) {
+ m_nameToCanonicalName.erase(iter++);
+ }
+ else {
+ ++index;
+ }
+ }
+}
+
+void
+Config::removeAllScreens()
+{
+ m_map.clear();
+ m_nameToCanonicalName.clear();
+}
+
+bool
+Config::addAlias(const String& canonical, const String& alias)
+{
+ // alias name must not exist
+ if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) {
+ return false;
+ }
+
+ // canonical name must be known
+ if (m_map.find(canonical) == m_map.end()) {
+ return false;
+ }
+
+ // insert alias
+ m_nameToCanonicalName.insert(std::make_pair(alias, canonical));
+
+ return true;
+}
+
+bool
+Config::removeAlias(const String& alias)
+{
+ // must not be a canonical name
+ if (m_map.find(alias) != m_map.end()) {
+ return false;
+ }
+
+ // find alias
+ NameMap::iterator index = m_nameToCanonicalName.find(alias);
+ if (index == m_nameToCanonicalName.end()) {
+ return false;
+ }
+
+ // remove alias
+ m_nameToCanonicalName.erase(index);
+
+ return true;
+}
+
+bool
+Config::removeAliases(const String& canonical)
+{
+ // must be a canonical name
+ if (m_map.find(canonical) == m_map.end()) {
+ return false;
+ }
+
+ // find and removing matching aliases
+ for (NameMap::iterator index = m_nameToCanonicalName.begin();
+ index != m_nameToCanonicalName.end(); ) {
+ if (index->second == canonical && index->first != canonical) {
+ m_nameToCanonicalName.erase(index++);
+ }
+ else {
+ ++index;
+ }
+ }
+
+ return true;
+}
+
+void
+Config::removeAllAliases()
+{
+ // remove all names
+ m_nameToCanonicalName.clear();
+
+ // put the canonical names back in
+ for (CellMap::iterator index = m_map.begin();
+ index != m_map.end(); ++index) {
+ m_nameToCanonicalName.insert(
+ std::make_pair(index->first, index->first));
+ }
+}
+
+bool
+Config::connect(const String& srcName,
+ EDirection srcSide,
+ float srcStart, float srcEnd,
+ const String& dstName,
+ float dstStart, float dstEnd)
+{
+ assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
+
+ // find source cell
+ CellMap::iterator index = m_map.find(getCanonicalName(srcName));
+ if (index == m_map.end()) {
+ return false;
+ }
+
+ // add link
+ CellEdge srcEdge(srcSide, Interval(srcStart, srcEnd));
+ CellEdge dstEdge(dstName, srcSide, Interval(dstStart, dstEnd));
+ return index->second.add(srcEdge, dstEdge);
+}
+
+bool
+Config::disconnect(const String& srcName, EDirection srcSide)
+{
+ assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
+
+ // find source cell
+ CellMap::iterator index = m_map.find(srcName);
+ if (index == m_map.end()) {
+ return false;
+ }
+
+ // disconnect side
+ index->second.remove(srcSide);
+
+ return true;
+}
+
+bool
+Config::disconnect(const String& srcName, EDirection srcSide, float position)
+{
+ assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
+
+ // find source cell
+ CellMap::iterator index = m_map.find(srcName);
+ if (index == m_map.end()) {
+ return false;
+ }
+
+ // disconnect side
+ index->second.remove(srcSide, position);
+
+ return true;
+}
+
+void
+Config::setBarrierAddress(const NetworkAddress& addr)
+{
+ m_barrierAddress = addr;
+}
+
+bool
+Config::addOption(const String& name, OptionID option, OptionValue value)
+{
+ // find options
+ ScreenOptions* options = NULL;
+ if (name.empty()) {
+ options = &m_globalOptions;
+ }
+ else {
+ CellMap::iterator index = m_map.find(name);
+ if (index != m_map.end()) {
+ options = &index->second.m_options;
+ }
+ }
+ if (options == NULL) {
+ return false;
+ }
+
+ // add option
+ options->insert(std::make_pair(option, value));
+ return true;
+}
+
+bool
+Config::removeOption(const String& name, OptionID option)
+{
+ // find options
+ ScreenOptions* options = NULL;
+ if (name.empty()) {
+ options = &m_globalOptions;
+ }
+ else {
+ CellMap::iterator index = m_map.find(name);
+ if (index != m_map.end()) {
+ options = &index->second.m_options;
+ }
+ }
+ if (options == NULL) {
+ return false;
+ }
+
+ // remove option
+ options->erase(option);
+ return true;
+}
+
+bool
+Config::removeOptions(const String& name)
+{
+ // find options
+ ScreenOptions* options = NULL;
+ if (name.empty()) {
+ options = &m_globalOptions;
+ }
+ else {
+ CellMap::iterator index = m_map.find(name);
+ if (index != m_map.end()) {
+ options = &index->second.m_options;
+ }
+ }
+ if (options == NULL) {
+ return false;
+ }
+
+ // remove options
+ options->clear();
+ return true;
+}
+
+bool
+Config::isValidScreenName(const String& name) const
+{
+ // name is valid if matches validname
+ // name ::= [_A-Za-z0-9] | [_A-Za-z0-9][-_A-Za-z0-9]*[_A-Za-z0-9]
+ // domain ::= . name
+ // validname ::= name domain*
+ // we also accept names ending in . because many OS X users have
+ // so misconfigured their systems.
+
+ // empty name is invalid
+ if (name.empty()) {
+ return false;
+ }
+
+ // check each dot separated part
+ String::size_type b = 0;
+ for (;;) {
+ // accept trailing .
+ if (b == name.size()) {
+ break;
+ }
+
+ // find end of part
+ String::size_type e = name.find('.', b);
+ if (e == String::npos) {
+ e = name.size();
+ }
+
+ // part may not be empty
+ if (e - b < 1) {
+ return false;
+ }
+
+ // check first and last characters
+ if (!(isalnum(name[b]) || name[b] == '_') ||
+ !(isalnum(name[e - 1]) || name[e - 1] == '_')) {
+ return false;
+ }
+
+ // check interior characters
+ for (String::size_type i = b; i < e; ++i) {
+ if (!isalnum(name[i]) && name[i] != '_' && name[i] != '-') {
+ return false;
+ }
+ }
+
+ // next part
+ if (e == name.size()) {
+ // no more parts
+ break;
+ }
+ b = e + 1;
+ }
+
+ return true;
+}
+
+Config::const_iterator
+Config::begin() const
+{
+ return const_iterator(m_map.begin());
+}
+
+Config::const_iterator
+Config::end() const
+{
+ return const_iterator(m_map.end());
+}
+
+Config::all_const_iterator
+Config::beginAll() const
+{
+ return m_nameToCanonicalName.begin();
+}
+
+Config::all_const_iterator
+Config::endAll() const
+{
+ return m_nameToCanonicalName.end();
+}
+
+bool
+Config::isScreen(const String& name) const
+{
+ return (m_nameToCanonicalName.count(name) > 0);
+}
+
+bool
+Config::isCanonicalName(const String& name) const
+{
+ return (!name.empty() &&
+ CaselessCmp::equal(getCanonicalName(name), name));
+}
+
+String
+Config::getCanonicalName(const String& name) const
+{
+ NameMap::const_iterator index = m_nameToCanonicalName.find(name);
+ if (index == m_nameToCanonicalName.end()) {
+ return String();
+ }
+ else {
+ return index->second;
+ }
+}
+
+String
+Config::getNeighbor(const String& srcName, EDirection srcSide,
+ float position, float* positionOut) const
+{
+ assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
+
+ // find source cell
+ CellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
+ if (index == m_map.end()) {
+ return String();
+ }
+
+ // find edge
+ const CellEdge* srcEdge, *dstEdge;
+ if (!index->second.getLink(srcSide, position, srcEdge, dstEdge)) {
+ // no neighbor
+ return "";
+ }
+ else {
+ // compute position on neighbor
+ if (positionOut != NULL) {
+ *positionOut =
+ dstEdge->inverseTransform(srcEdge->transform(position));
+ }
+
+ // return neighbor's name
+ return getCanonicalName(dstEdge->getName());
+ }
+}
+
+bool
+Config::hasNeighbor(const String& srcName, EDirection srcSide) const
+{
+ return hasNeighbor(srcName, srcSide, 0.0f, 1.0f);
+}
+
+bool
+Config::hasNeighbor(const String& srcName, EDirection srcSide,
+ float start, float end) const
+{
+ assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
+
+ // find source cell
+ CellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
+ if (index == m_map.end()) {
+ return false;
+ }
+
+ return index->second.overlaps(CellEdge(srcSide, Interval(start, end)));
+}
+
+Config::link_const_iterator
+Config::beginNeighbor(const String& srcName) const
+{
+ CellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
+ assert(index != m_map.end());
+ return index->second.begin();
+}
+
+Config::link_const_iterator
+Config::endNeighbor(const String& srcName) const
+{
+ CellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
+ assert(index != m_map.end());
+ return index->second.end();
+}
+
+const NetworkAddress&
+Config::getBarrierAddress() const
+{
+ return m_barrierAddress;
+}
+
+const Config::ScreenOptions*
+Config::getOptions(const String& name) const
+{
+ // find options
+ const ScreenOptions* options = NULL;
+ if (name.empty()) {
+ options = &m_globalOptions;
+ }
+ else {
+ CellMap::const_iterator index = m_map.find(name);
+ if (index != m_map.end()) {
+ options = &index->second.m_options;
+ }
+ }
+
+ // return options
+ return options;
+}
+
+bool
+Config::hasLockToScreenAction() const
+{
+ return m_hasLockToScreenAction;
+}
+
+bool
+Config::operator==(const Config& x) const
+{
+ if (m_barrierAddress != x.m_barrierAddress) {
+ return false;
+ }
+ if (m_map.size() != x.m_map.size()) {
+ return false;
+ }
+ if (m_nameToCanonicalName.size() != x.m_nameToCanonicalName.size()) {
+ return false;
+ }
+
+ // compare global options
+ if (m_globalOptions != x.m_globalOptions) {
+ return false;
+ }
+
+ for (CellMap::const_iterator index1 = m_map.begin(),
+ index2 = x.m_map.begin();
+ index1 != m_map.end(); ++index1, ++index2) {
+ // compare names
+ if (!CaselessCmp::equal(index1->first, index2->first)) {
+ return false;
+ }
+
+ // compare cells
+ if (index1->second != index2->second) {
+ return false;
+ }
+ }
+
+ for (NameMap::const_iterator index1 = m_nameToCanonicalName.begin(),
+ index2 = x.m_nameToCanonicalName.begin();
+ index1 != m_nameToCanonicalName.end();
+ ++index1, ++index2) {
+ if (!CaselessCmp::equal(index1->first, index2->first) ||
+ !CaselessCmp::equal(index1->second, index2->second)) {
+ return false;
+ }
+ }
+
+ // compare input filters
+ if (m_inputFilter != x.m_inputFilter) {
+ return false;
+ }
+
+ return true;
+}
+
+bool
+Config::operator!=(const Config& x) const
+{
+ return !operator==(x);
+}
+
+void
+Config::read(ConfigReadContext& context)
+{
+ Config tmp(m_events);
+ while (context.getStream()) {
+ tmp.readSection(context);
+ }
+ *this = tmp;
+}
+
+const char*
+Config::dirName(EDirection dir)
+{
+ static const char* s_name[] = { "left", "right", "up", "down" };
+
+ assert(dir >= kFirstDirection && dir <= kLastDirection);
+
+ return s_name[dir - kFirstDirection];
+}
+
+InputFilter*
+Config::getInputFilter()
+{
+ return &m_inputFilter;
+}
+
+String
+Config::formatInterval(const Interval& x)
+{
+ if (x.first == 0.0f && x.second == 1.0f) {
+ return "";
+ }
+ return barrier::string::sprintf("(%d,%d)", (int)(x.first * 100.0f + 0.5f),
+ (int)(x.second * 100.0f + 0.5f));
+}
+
+void
+Config::readSection(ConfigReadContext& s)
+{
+ static const char s_section[] = "section:";
+ static const char s_options[] = "options";
+ static const char s_screens[] = "screens";
+ static const char s_links[] = "links";
+ static const char s_aliases[] = "aliases";
+
+ String line;
+ if (!s.readLine(line)) {
+ // no more sections
+ return;
+ }
+
+ // should be a section header
+ if (line.find(s_section) != 0) {
+ throw XConfigRead(s, "found data outside section");
+ }
+
+ // get section name
+ String::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1);
+ if (i == String::npos) {
+ throw XConfigRead(s, "section name is missing");
+ }
+ String name = line.substr(i);
+ i = name.find_first_of(" \t");
+ if (i != String::npos) {
+ throw XConfigRead(s, "unexpected data after section name");
+ }
+
+ // read section
+ if (name == s_options) {
+ readSectionOptions(s);
+ }
+ else if (name == s_screens) {
+ readSectionScreens(s);
+ }
+ else if (name == s_links) {
+ readSectionLinks(s);
+ }
+ else if (name == s_aliases) {
+ readSectionAliases(s);
+ }
+ else {
+ throw XConfigRead(s, "unknown section name \"%{1}\"", name);
+ }
+}
+
+void
+Config::readSectionOptions(ConfigReadContext& s)
+{
+ String line;
+ while (s.readLine(line)) {
+ // check for end of section
+ if (line == "end") {
+ return;
+ }
+
+ // parse argument: `nameAndArgs = [values][;[values]]'
+ // nameAndArgs := <name>[(arg[,...])]
+ // values := valueAndArgs[,valueAndArgs]...
+ // valueAndArgs := <value>[(arg[,...])]
+ String::size_type i = 0;
+ String name, value;
+ ConfigReadContext::ArgList nameArgs, valueArgs;
+ s.parseNameWithArgs("name", line, "=", i, name, nameArgs);
+ ++i;
+ s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs);
+
+ bool handled = true;
+ if (name == "address") {
+ try {
+ m_barrierAddress = NetworkAddress(value, kDefaultPort);
+ m_barrierAddress.resolve();
+ }
+ catch (XSocketAddress& e) {
+ throw XConfigRead(s,
+ String("invalid address argument ") + e.what());
+ }
+ }
+ else if (name == "heartbeat") {
+ addOption("", kOptionHeartbeat, s.parseInt(value));
+ }
+ else if (name == "switchCorners") {
+ addOption("", kOptionScreenSwitchCorners, s.parseCorners(value));
+ }
+ else if (name == "switchCornerSize") {
+ addOption("", kOptionScreenSwitchCornerSize, s.parseInt(value));
+ }
+ else if (name == "switchDelay") {
+ addOption("", kOptionScreenSwitchDelay, s.parseInt(value));
+ }
+ else if (name == "switchDoubleTap") {
+ addOption("", kOptionScreenSwitchTwoTap, s.parseInt(value));
+ }
+ else if (name == "switchNeedsShift") {
+ addOption("", kOptionScreenSwitchNeedsShift, s.parseBoolean(value));
+ }
+ else if (name == "switchNeedsControl") {
+ addOption("", kOptionScreenSwitchNeedsControl, s.parseBoolean(value));
+ }
+ else if (name == "switchNeedsAlt") {
+ addOption("", kOptionScreenSwitchNeedsAlt, s.parseBoolean(value));
+ }
+ else if (name == "screenSaverSync") {
+ addOption("", kOptionScreenSaverSync, s.parseBoolean(value));
+ }
+ else if (name == "relativeMouseMoves") {
+ addOption("", kOptionRelativeMouseMoves, s.parseBoolean(value));
+ }
+ else if (name == "win32KeepForeground") {
+ addOption("", kOptionWin32KeepForeground, s.parseBoolean(value));
+ }
+ else if (name == "clipboardSharing") {
+ addOption("", kOptionClipboardSharing, s.parseBoolean(value));
+ }
+
+ else {
+ handled = false;
+ }
+
+ if (handled) {
+ // make sure handled options aren't followed by more values
+ if (i < line.size() && (line[i] == ',' || line[i] == ';')) {
+ throw XConfigRead(s, "to many arguments to %s", name.c_str());
+ }
+ }
+ else {
+ // make filter rule
+ InputFilter::Rule rule(parseCondition(s, name, nameArgs));
+
+ // save first action (if any)
+ if (!value.empty() || line[i] != ';') {
+ parseAction(s, value, valueArgs, rule, true);
+ }
+
+ // get remaining activate actions
+ while (i < line.length() && line[i] != ';') {
+ ++i;
+ s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs);
+ parseAction(s, value, valueArgs, rule, true);
+ }
+
+ // get deactivate actions
+ if (i < line.length() && line[i] == ';') {
+ // allow trailing ';'
+ i = line.find_first_not_of(" \t", i + 1);
+ if (i == String::npos) {
+ i = line.length();
+ }
+ else {
+ --i;
+ }
+
+ // get actions
+ while (i < line.length()) {
+ ++i;
+ s.parseNameWithArgs("value", line, ",\n",
+ i, value, valueArgs);
+ parseAction(s, value, valueArgs, rule, false);
+ }
+ }
+
+ // add rule
+ m_inputFilter.addFilterRule(rule);
+ }
+ }
+ throw XConfigRead(s, "unexpected end of options section");
+}
+
+void
+Config::readSectionScreens(ConfigReadContext& s)
+{
+ String line;
+ String screen;
+ while (s.readLine(line)) {
+ // check for end of section
+ if (line == "end") {
+ return;
+ }
+
+ // see if it's the next screen
+ if (line[line.size() - 1] == ':') {
+ // strip :
+ screen = line.substr(0, line.size() - 1);
+
+ // verify validity of screen name
+ if (!isValidScreenName(screen)) {
+ throw XConfigRead(s, "invalid screen name \"%{1}\"", screen);
+ }
+
+ // add the screen to the configuration
+ if (!addScreen(screen)) {
+ throw XConfigRead(s, "duplicate screen name \"%{1}\"", screen);
+ }
+ }
+ else if (screen.empty()) {
+ throw XConfigRead(s, "argument before first screen");
+ }
+ else {
+ // parse argument: `<name>=<value>'
+ String::size_type i = line.find_first_of(" \t=");
+ if (i == 0) {
+ throw XConfigRead(s, "missing argument name");
+ }
+ if (i == String::npos) {
+ throw XConfigRead(s, "missing =");
+ }
+ String name = line.substr(0, i);
+ i = line.find_first_not_of(" \t", i);
+ if (i == String::npos || line[i] != '=') {
+ throw XConfigRead(s, "missing =");
+ }
+ i = line.find_first_not_of(" \t", i + 1);
+ String value;
+ if (i != String::npos) {
+ value = line.substr(i);
+ }
+
+ // handle argument
+ if (name == "halfDuplexCapsLock") {
+ addOption(screen, kOptionHalfDuplexCapsLock,
+ s.parseBoolean(value));
+ }
+ else if (name == "halfDuplexNumLock") {
+ addOption(screen, kOptionHalfDuplexNumLock,
+ s.parseBoolean(value));
+ }
+ else if (name == "halfDuplexScrollLock") {
+ addOption(screen, kOptionHalfDuplexScrollLock,
+ s.parseBoolean(value));
+ }
+ else if (name == "shift") {
+ addOption(screen, kOptionModifierMapForShift,
+ s.parseModifierKey(value));
+ }
+ else if (name == "ctrl") {
+ addOption(screen, kOptionModifierMapForControl,
+ s.parseModifierKey(value));
+ }
+ else if (name == "alt") {
+ addOption(screen, kOptionModifierMapForAlt,
+ s.parseModifierKey(value));
+ }
+ else if (name == "altgr") {
+ addOption(screen, kOptionModifierMapForAltGr,
+ s.parseModifierKey(value));
+ }
+ else if (name == "meta") {
+ addOption(screen, kOptionModifierMapForMeta,
+ s.parseModifierKey(value));
+ }
+ else if (name == "super") {
+ addOption(screen, kOptionModifierMapForSuper,
+ s.parseModifierKey(value));
+ }
+ else if (name == "xtestIsXineramaUnaware") {
+ addOption(screen, kOptionXTestXineramaUnaware,
+ s.parseBoolean(value));
+ }
+ else if (name == "switchCorners") {
+ addOption(screen, kOptionScreenSwitchCorners,
+ s.parseCorners(value));
+ }
+ else if (name == "switchCornerSize") {
+ addOption(screen, kOptionScreenSwitchCornerSize,
+ s.parseInt(value));
+ }
+ else if (name == "preserveFocus") {
+ addOption(screen, kOptionScreenPreserveFocus,
+ s.parseBoolean(value));
+ }
+ else {
+ // unknown argument
+ throw XConfigRead(s, "unknown argument \"%{1}\"", name);
+ }
+ }
+ }
+ throw XConfigRead(s, "unexpected end of screens section");
+}
+
+void
+Config::readSectionLinks(ConfigReadContext& s)
+{
+ String line;
+ String screen;
+ while (s.readLine(line)) {
+ // check for end of section
+ if (line == "end") {
+ return;
+ }
+
+ // see if it's the next screen
+ if (line[line.size() - 1] == ':') {
+ // strip :
+ screen = line.substr(0, line.size() - 1);
+
+ // verify we know about the screen
+ if (!isScreen(screen)) {
+ throw XConfigRead(s, "unknown screen name \"%{1}\"", screen);
+ }
+ if (!isCanonicalName(screen)) {
+ throw XConfigRead(s, "cannot use screen name alias here");
+ }
+ }
+ else if (screen.empty()) {
+ throw XConfigRead(s, "argument before first screen");
+ }
+ else {
+ // parse argument: `<name>[(<s0>,<e0>)]=<value>[(<s1>,<e1>)]'
+ // the stuff in brackets is optional. interval values must be
+ // in the range [0,100] and start < end. if not given the
+ // interval is taken to be (0,100).
+ String::size_type i = 0;
+ String side, dstScreen, srcArgString, dstArgString;
+ ConfigReadContext::ArgList srcArgs, dstArgs;
+ s.parseNameWithArgs("link", line, "=", i, side, srcArgs);
+ ++i;
+ s.parseNameWithArgs("screen", line, "", i, dstScreen, dstArgs);
+ Interval srcInterval(s.parseInterval(srcArgs));
+ Interval dstInterval(s.parseInterval(dstArgs));
+
+ // handle argument
+ EDirection dir;
+ if (side == "left") {
+ dir = kLeft;
+ }
+ else if (side == "right") {
+ dir = kRight;
+ }
+ else if (side == "up") {
+ dir = kTop;
+ }
+ else if (side == "down") {
+ dir = kBottom;
+ }
+ else {
+ // unknown argument
+ throw XConfigRead(s, "unknown side \"%{1}\" in link", side);
+ }
+ if (!isScreen(dstScreen)) {
+ throw XConfigRead(s, "unknown screen name \"%{1}\"", dstScreen);
+ }
+ if (!connect(screen, dir,
+ srcInterval.first, srcInterval.second,
+ dstScreen,
+ dstInterval.first, dstInterval.second)) {
+ throw XConfigRead(s, "overlapping range");
+ }
+ }
+ }
+ throw XConfigRead(s, "unexpected end of links section");
+}
+
+void
+Config::readSectionAliases(ConfigReadContext& s)
+{
+ String line;
+ String screen;
+ while (s.readLine(line)) {
+ // check for end of section
+ if (line == "end") {
+ return;
+ }
+
+ // see if it's the next screen
+ if (line[line.size() - 1] == ':') {
+ // strip :
+ screen = line.substr(0, line.size() - 1);
+
+ // verify we know about the screen
+ if (!isScreen(screen)) {
+ throw XConfigRead(s, "unknown screen name \"%{1}\"", screen);
+ }
+ if (!isCanonicalName(screen)) {
+ throw XConfigRead(s, "cannot use screen name alias here");
+ }
+ }
+ else if (screen.empty()) {
+ throw XConfigRead(s, "argument before first screen");
+ }
+ else {
+ // verify validity of screen name
+ if (!isValidScreenName(line)) {
+ throw XConfigRead(s, "invalid screen alias \"%{1}\"", line);
+ }
+
+ // add alias
+ if (!addAlias(screen, line)) {
+ throw XConfigRead(s, "alias \"%{1}\" is already used", line);
+ }
+ }
+ }
+ throw XConfigRead(s, "unexpected end of aliases section");
+}
+
+
+InputFilter::Condition*
+Config::parseCondition(ConfigReadContext& s,
+ const String& name, const std::vector<String>& args)
+{
+ if (name == "keystroke") {
+ if (args.size() != 1) {
+ throw XConfigRead(s, "syntax for condition: keystroke(modifiers+key)");
+ }
+
+ IPlatformScreen::KeyInfo* keyInfo = s.parseKeystroke(args[0]);
+
+ return new InputFilter::KeystrokeCondition(m_events, keyInfo);
+ }
+
+ if (name == "mousebutton") {
+ if (args.size() != 1) {
+ throw XConfigRead(s, "syntax for condition: mousebutton(modifiers+button)");
+ }
+
+ IPlatformScreen::ButtonInfo* mouseInfo = s.parseMouse(args[0]);
+
+ return new InputFilter::MouseButtonCondition(m_events, mouseInfo);
+ }
+
+ if (name == "connect") {
+ if (args.size() != 1) {
+ throw XConfigRead(s, "syntax for condition: connect([screen])");
+ }
+
+ String screen = args[0];
+ if (isScreen(screen)) {
+ screen = getCanonicalName(screen);
+ }
+ else if (!screen.empty()) {
+ throw XConfigRead(s, "unknown screen name \"%{1}\" in connect", screen);
+ }
+
+ return new InputFilter::ScreenConnectedCondition(m_events, screen);
+ }
+
+ throw XConfigRead(s, "unknown argument \"%{1}\"", name);
+}
+
+void
+Config::parseAction(ConfigReadContext& s,
+ const String& name, const std::vector<String>& args,
+ InputFilter::Rule& rule, bool activate)
+{
+ InputFilter::Action* action;
+
+ if (name == "keystroke" || name == "keyDown" || name == "keyUp") {
+ if (args.size() < 1 || args.size() > 2) {
+ throw XConfigRead(s, "syntax for action: keystroke(modifiers+key[,screens])");
+ }
+
+ IPlatformScreen::KeyInfo* keyInfo;
+ if (args.size() == 1) {
+ keyInfo = s.parseKeystroke(args[0]);
+ }
+ else {
+ std::set<String> screens;
+ parseScreens(s, args[1], screens);
+ keyInfo = s.parseKeystroke(args[0], screens);
+ }
+
+ if (name == "keystroke") {
+ IPlatformScreen::KeyInfo* keyInfo2 =
+ IKeyState::KeyInfo::alloc(*keyInfo);
+ action = new InputFilter::KeystrokeAction(m_events, keyInfo2, true);
+ rule.adoptAction(action, true);
+ action = new InputFilter::KeystrokeAction(m_events, keyInfo, false);
+ activate = false;
+ }
+ else if (name == "keyDown") {
+ action = new InputFilter::KeystrokeAction(m_events, keyInfo, true);
+ }
+ else {
+ action = new InputFilter::KeystrokeAction(m_events, keyInfo, false);
+ }
+ }
+
+ else if (name == "mousebutton" ||
+ name == "mouseDown" || name == "mouseUp") {
+ if (args.size() != 1) {
+ throw XConfigRead(s, "syntax for action: mousebutton(modifiers+button)");
+ }
+
+ IPlatformScreen::ButtonInfo* mouseInfo = s.parseMouse(args[0]);
+
+ if (name == "mousebutton") {
+ IPlatformScreen::ButtonInfo* mouseInfo2 =
+ IPlatformScreen::ButtonInfo::alloc(*mouseInfo);
+ action = new InputFilter::MouseButtonAction(m_events, mouseInfo2, true);
+ rule.adoptAction(action, true);
+ action = new InputFilter::MouseButtonAction(m_events, mouseInfo, false);
+ activate = false;
+ }
+ else if (name == "mouseDown") {
+ action = new InputFilter::MouseButtonAction(m_events, mouseInfo, true);
+ }
+ else {
+ action = new InputFilter::MouseButtonAction(m_events, mouseInfo, false);
+ }
+ }
+
+/* XXX -- not supported
+ else if (name == "modifier") {
+ if (args.size() != 1) {
+ throw XConfigRead(s, "syntax for action: modifier(modifiers)");
+ }
+
+ KeyModifierMask mask = s.parseModifier(args[0]);
+
+ action = new InputFilter::ModifierAction(mask, ~mask);
+ }
+*/
+
+ else if (name == "switchToScreen") {
+ if (args.size() != 1) {
+ throw XConfigRead(s, "syntax for action: switchToScreen(name)");
+ }
+
+ String screen = args[0];
+ if (isScreen(screen)) {
+ screen = getCanonicalName(screen);
+ }
+ else if (!screen.empty()) {
+ throw XConfigRead(s, "unknown screen name in switchToScreen");
+ }
+
+ action = new InputFilter::SwitchToScreenAction(m_events, screen);
+ }
+
+ else if (name == "switchInDirection") {
+ if (args.size() != 1) {
+ throw XConfigRead(s, "syntax for action: switchInDirection(<left|right|up|down>)");
+ }
+
+ EDirection direction;
+ if (args[0] == "left") {
+ direction = kLeft;
+ }
+ else if (args[0] == "right") {
+ direction = kRight;
+ }
+ else if (args[0] == "up") {
+ direction = kTop;
+ }
+ else if (args[0] == "down") {
+ direction = kBottom;
+ }
+ else {
+ throw XConfigRead(s, "unknown direction \"%{1}\" in switchToScreen", args[0]);
+ }
+
+ action = new InputFilter::SwitchInDirectionAction(m_events, direction);
+ }
+
+ else if (name == "lockCursorToScreen") {
+ if (args.size() > 1) {
+ throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])");
+ }
+
+ InputFilter::LockCursorToScreenAction::Mode mode =
+ InputFilter::LockCursorToScreenAction::kToggle;
+ if (args.size() == 1) {
+ if (args[0] == "off") {
+ mode = InputFilter::LockCursorToScreenAction::kOff;
+ }
+ else if (args[0] == "on") {
+ mode = InputFilter::LockCursorToScreenAction::kOn;
+ }
+ else if (args[0] == "toggle") {
+ mode = InputFilter::LockCursorToScreenAction::kToggle;
+ }
+ else {
+ throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])");
+ }
+ }
+
+ if (mode != InputFilter::LockCursorToScreenAction::kOff) {
+ m_hasLockToScreenAction = true;
+ }
+
+ action = new InputFilter::LockCursorToScreenAction(m_events, mode);
+ }
+
+ else if (name == "keyboardBroadcast") {
+ if (args.size() > 2) {
+ throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])");
+ }
+
+ InputFilter::KeyboardBroadcastAction::Mode mode =
+ InputFilter::KeyboardBroadcastAction::kToggle;
+ if (args.size() >= 1) {
+ if (args[0] == "off") {
+ mode = InputFilter::KeyboardBroadcastAction::kOff;
+ }
+ else if (args[0] == "on") {
+ mode = InputFilter::KeyboardBroadcastAction::kOn;
+ }
+ else if (args[0] == "toggle") {
+ mode = InputFilter::KeyboardBroadcastAction::kToggle;
+ }
+ else {
+ throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])");
+ }
+ }
+
+ std::set<String> screens;
+ if (args.size() >= 2) {
+ parseScreens(s, args[1], screens);
+ }
+
+ action = new InputFilter::KeyboardBroadcastAction(m_events, mode, screens);
+ }
+
+ else {
+ throw XConfigRead(s, "unknown action argument \"%{1}\"", name);
+ }
+
+ rule.adoptAction(action, activate);
+}
+
+void
+Config::parseScreens(ConfigReadContext& c,
+ const String& s, std::set<String>& screens) const
+{
+ screens.clear();
+
+ String::size_type i = 0;
+ while (i < s.size()) {
+ // find end of next screen name
+ String::size_type j = s.find(':', i);
+ if (j == String::npos) {
+ j = s.size();
+ }
+
+ // extract name
+ String rawName;
+ i = s.find_first_not_of(" \t", i);
+ if (i < j) {
+ rawName = s.substr(i, s.find_last_not_of(" \t", j - 1) - i + 1);
+ }
+
+ // add name
+ if (rawName == "*") {
+ screens.insert("*");
+ }
+ else if (!rawName.empty()) {
+ String name = getCanonicalName(rawName);
+ if (name.empty()) {
+ throw XConfigRead(c, "unknown screen name \"%{1}\"", rawName);
+ }
+ screens.insert(name);
+ }
+
+ // next
+ i = j + 1;
+ }
+}
+
+const char*
+Config::getOptionName(OptionID id)
+{
+ if (id == kOptionHalfDuplexCapsLock) {
+ return "halfDuplexCapsLock";
+ }
+ if (id == kOptionHalfDuplexNumLock) {
+ return "halfDuplexNumLock";
+ }
+ if (id == kOptionHalfDuplexScrollLock) {
+ return "halfDuplexScrollLock";
+ }
+ if (id == kOptionModifierMapForShift) {
+ return "shift";
+ }
+ if (id == kOptionModifierMapForControl) {
+ return "ctrl";
+ }
+ if (id == kOptionModifierMapForAlt) {
+ return "alt";
+ }
+ if (id == kOptionModifierMapForAltGr) {
+ return "altgr";
+ }
+ if (id == kOptionModifierMapForMeta) {
+ return "meta";
+ }
+ if (id == kOptionModifierMapForSuper) {
+ return "super";
+ }
+ if (id == kOptionHeartbeat) {
+ return "heartbeat";
+ }
+ if (id == kOptionScreenSwitchCorners) {
+ return "switchCorners";
+ }
+ if (id == kOptionScreenSwitchCornerSize) {
+ return "switchCornerSize";
+ }
+ if (id == kOptionScreenSwitchDelay) {
+ return "switchDelay";
+ }
+ if (id == kOptionScreenSwitchTwoTap) {
+ return "switchDoubleTap";
+ }
+ if (id == kOptionScreenSwitchNeedsShift) {
+ return "switchNeedsShift";
+ }
+ if (id == kOptionScreenSwitchNeedsControl) {
+ return "switchNeedsControl";
+ }
+ if (id == kOptionScreenSwitchNeedsAlt) {
+ return "switchNeedsAlt";
+ }
+ if (id == kOptionScreenSaverSync) {
+ return "screenSaverSync";
+ }
+ if (id == kOptionXTestXineramaUnaware) {
+ return "xtestIsXineramaUnaware";
+ }
+ if (id == kOptionRelativeMouseMoves) {
+ return "relativeMouseMoves";
+ }
+ if (id == kOptionWin32KeepForeground) {
+ return "win32KeepForeground";
+ }
+ if (id == kOptionScreenPreserveFocus) {
+ return "preserveFocus";
+ }
+ if (id == kOptionClipboardSharing) {
+ return "clipboardSharing";
+ }
+ return NULL;
+}
+
+String
+Config::getOptionValue(OptionID id, OptionValue value)
+{
+ if (id == kOptionHalfDuplexCapsLock ||
+ id == kOptionHalfDuplexNumLock ||
+ id == kOptionHalfDuplexScrollLock ||
+ id == kOptionScreenSwitchNeedsShift ||
+ id == kOptionScreenSwitchNeedsControl ||
+ id == kOptionScreenSwitchNeedsAlt ||
+ id == kOptionScreenSaverSync ||
+ id == kOptionXTestXineramaUnaware ||
+ id == kOptionRelativeMouseMoves ||
+ id == kOptionWin32KeepForeground ||
+ id == kOptionScreenPreserveFocus ||
+ id == kOptionClipboardSharing) {
+ return (value != 0) ? "true" : "false";
+ }
+ if (id == kOptionModifierMapForShift ||
+ id == kOptionModifierMapForControl ||
+ id == kOptionModifierMapForAlt ||
+ id == kOptionModifierMapForAltGr ||
+ id == kOptionModifierMapForMeta ||
+ id == kOptionModifierMapForSuper) {
+ switch (value) {
+ case kKeyModifierIDShift:
+ return "shift";
+
+ case kKeyModifierIDControl:
+ return "ctrl";
+
+ case kKeyModifierIDAlt:
+ return "alt";
+
+ case kKeyModifierIDAltGr:
+ return "altgr";
+
+ case kKeyModifierIDMeta:
+ return "meta";
+
+ case kKeyModifierIDSuper:
+ return "super";
+
+ default:
+ return "none";
+ }
+ }
+ if (id == kOptionHeartbeat ||
+ id == kOptionScreenSwitchCornerSize ||
+ id == kOptionScreenSwitchDelay ||
+ id == kOptionScreenSwitchTwoTap) {
+ return barrier::string::sprintf("%d", value);
+ }
+ if (id == kOptionScreenSwitchCorners) {
+ std::string result("none");
+ if ((value & kTopLeftMask) != 0) {
+ result += " +top-left";
+ }
+ if ((value & kTopRightMask) != 0) {
+ result += " +top-right";
+ }
+ if ((value & kBottomLeftMask) != 0) {
+ result += " +bottom-left";
+ }
+ if ((value & kBottomRightMask) != 0) {
+ result += " +bottom-right";
+ }
+ return result;
+ }
+
+ return "";
+}
+
+
+//
+// Config::Name
+//
+
+Config::Name::Name(Config* config, const String& name) :
+ m_config(config),
+ m_name(config->getCanonicalName(name))
+{
+ // do nothing
+}
+
+bool
+Config::Name::operator==(const String& name) const
+{
+ String canonical = m_config->getCanonicalName(name);
+ return CaselessCmp::equal(canonical, m_name);
+}
+
+
+//
+// Config::CellEdge
+//
+
+Config::CellEdge::CellEdge(EDirection side, float position)
+{
+ init("", side, Interval(position, position));
+}
+
+Config::CellEdge::CellEdge(EDirection side, const Interval& interval)
+{
+ assert(interval.first >= 0.0f);
+ assert(interval.second <= 1.0f);
+ assert(interval.first < interval.second);
+
+ init("", side, interval);
+}
+
+Config::CellEdge::CellEdge(const String& name,
+ EDirection side, const Interval& interval)
+{
+ assert(interval.first >= 0.0f);
+ assert(interval.second <= 1.0f);
+ assert(interval.first < interval.second);
+
+ init(name, side, interval);
+}
+
+Config::CellEdge::~CellEdge()
+{
+ // do nothing
+}
+
+void
+Config::CellEdge::init(const String& name, EDirection side,
+ const Interval& interval)
+{
+ assert(side != kNoDirection);
+
+ m_name = name;
+ m_side = side;
+ m_interval = interval;
+}
+
+Config::Interval
+Config::CellEdge::getInterval() const
+{
+ return m_interval;
+}
+
+void
+Config::CellEdge::setName(const String& newName)
+{
+ m_name = newName;
+}
+
+String
+Config::CellEdge::getName() const
+{
+ return m_name;
+}
+
+EDirection
+Config::CellEdge::getSide() const
+{
+ return m_side;
+}
+
+bool
+Config::CellEdge::overlaps(const CellEdge& edge) const
+{
+ const Interval& x = m_interval;
+ const Interval& y = edge.m_interval;
+ if (m_side != edge.m_side) {
+ return false;
+ }
+ return (x.first >= y.first && x.first < y.second) ||
+ (x.second > y.first && x.second <= y.second) ||
+ (y.first >= x.first && y.first < x.second) ||
+ (y.second > x.first && y.second <= x.second);
+}
+
+bool
+Config::CellEdge::isInside(float x) const
+{
+ return (x >= m_interval.first && x < m_interval.second);
+}
+
+float
+Config::CellEdge::transform(float x) const
+{
+ return (x - m_interval.first) / (m_interval.second - m_interval.first);
+}
+
+
+float
+Config::CellEdge::inverseTransform(float x) const
+{
+ return x * (m_interval.second - m_interval.first) + m_interval.first;
+}
+
+bool
+Config::CellEdge::operator<(const CellEdge& o) const
+{
+ if (static_cast<int>(m_side) < static_cast<int>(o.m_side)) {
+ return true;
+ }
+ else if (static_cast<int>(m_side) > static_cast<int>(o.m_side)) {
+ return false;
+ }
+
+ return (m_interval.first < o.m_interval.first);
+}
+
+bool
+Config::CellEdge::operator==(const CellEdge& x) const
+{
+ return (m_side == x.m_side && m_interval == x.m_interval);
+}
+
+bool
+Config::CellEdge::operator!=(const CellEdge& x) const
+{
+ return !operator==(x);
+}
+
+
+//
+// Config::Cell
+//
+
+bool
+Config::Cell::add(const CellEdge& src, const CellEdge& dst)
+{
+ // cannot add an edge that overlaps other existing edges but we
+ // can exactly replace an edge.
+ if (!hasEdge(src) && overlaps(src)) {
+ return false;
+ }
+
+ m_neighbors.erase(src);
+ m_neighbors.insert(std::make_pair(src, dst));
+ return true;
+}
+
+void
+Config::Cell::remove(EDirection side)
+{
+ for (EdgeLinks::iterator j = m_neighbors.begin();
+ j != m_neighbors.end(); ) {
+ if (j->first.getSide() == side) {
+ m_neighbors.erase(j++);
+ }
+ else {
+ ++j;
+ }
+ }
+}
+
+void
+Config::Cell::remove(EDirection side, float position)
+{
+ for (EdgeLinks::iterator j = m_neighbors.begin();
+ j != m_neighbors.end(); ++j) {
+ if (j->first.getSide() == side && j->first.isInside(position)) {
+ m_neighbors.erase(j);
+ break;
+ }
+ }
+}
+void
+Config::Cell::remove(const Name& name)
+{
+ for (EdgeLinks::iterator j = m_neighbors.begin();
+ j != m_neighbors.end(); ) {
+ if (name == j->second.getName()) {
+ m_neighbors.erase(j++);
+ }
+ else {
+ ++j;
+ }
+ }
+}
+
+void
+Config::Cell::rename(const Name& oldName, const String& newName)
+{
+ for (EdgeLinks::iterator j = m_neighbors.begin();
+ j != m_neighbors.end(); ++j) {
+ if (oldName == j->second.getName()) {
+ j->second.setName(newName);
+ }
+ }
+}
+
+bool
+Config::Cell::hasEdge(const CellEdge& edge) const
+{
+ EdgeLinks::const_iterator i = m_neighbors.find(edge);
+ return (i != m_neighbors.end() && i->first == edge);
+}
+
+bool
+Config::Cell::overlaps(const CellEdge& edge) const
+{
+ EdgeLinks::const_iterator i = m_neighbors.upper_bound(edge);
+ if (i != m_neighbors.end() && i->first.overlaps(edge)) {
+ return true;
+ }
+ if (i != m_neighbors.begin() && (--i)->first.overlaps(edge)) {
+ return true;
+ }
+ return false;
+}
+
+bool
+Config::Cell::getLink(EDirection side, float position,
+ const CellEdge*& src, const CellEdge*& dst) const
+{
+ CellEdge edge(side, position);
+ EdgeLinks::const_iterator i = m_neighbors.upper_bound(edge);
+ if (i == m_neighbors.begin()) {
+ return false;
+ }
+ --i;
+ if (i->first.getSide() == side && i->first.isInside(position)) {
+ src = &i->first;
+ dst = &i->second;
+ return true;
+ }
+ return false;
+}
+
+bool
+Config::Cell::operator==(const Cell& x) const
+{
+ // compare options
+ if (m_options != x.m_options) {
+ return false;
+ }
+
+ // compare links
+ if (m_neighbors.size() != x.m_neighbors.size()) {
+ return false;
+ }
+ for (EdgeLinks::const_iterator index1 = m_neighbors.begin(),
+ index2 = x.m_neighbors.begin();
+ index1 != m_neighbors.end();
+ ++index1, ++index2) {
+ if (index1->first != index2->first) {
+ return false;
+ }
+ if (index1->second != index2->second) {
+ return false;
+ }
+
+ // operator== doesn't compare names. only compare destination
+ // names.
+ if (!CaselessCmp::equal(index1->second.getName(),
+ index2->second.getName())) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+Config::Cell::operator!=(const Cell& x) const
+{
+ return !operator==(x);
+}
+
+Config::Cell::const_iterator
+Config::Cell::begin() const
+{
+ return m_neighbors.begin();
+}
+
+Config::Cell::const_iterator
+Config::Cell::end() const
+{
+ return m_neighbors.end();
+}
+
+
+//
+// Config I/O
+//
+
+std::istream&
+operator>>(std::istream& s, Config& config)
+{
+ ConfigReadContext context(s);
+ config.read(context);
+ return s;
+}
+
+std::ostream&
+operator<<(std::ostream& s, const Config& config)
+{
+ // screens section
+ s << "section: screens" << std::endl;
+ for (Config::const_iterator screen = config.begin();
+ screen != config.end(); ++screen) {
+ s << "\t" << screen->c_str() << ":" << std::endl;
+ const Config::ScreenOptions* options = config.getOptions(*screen);
+ if (options != NULL && options->size() > 0) {
+ for (Config::ScreenOptions::const_iterator
+ option = options->begin();
+ option != options->end(); ++option) {
+ const char* name = Config::getOptionName(option->first);
+ String value = Config::getOptionValue(option->first,
+ option->second);
+ if (name != NULL && !value.empty()) {
+ s << "\t\t" << name << " = " << value << std::endl;
+ }
+ }
+ }
+ }
+ s << "end" << std::endl;
+
+ // links section
+ String neighbor;
+ s << "section: links" << std::endl;
+ for (Config::const_iterator screen = config.begin();
+ screen != config.end(); ++screen) {
+ s << "\t" << screen->c_str() << ":" << std::endl;
+
+ for (Config::link_const_iterator
+ link = config.beginNeighbor(*screen),
+ nend = config.endNeighbor(*screen); link != nend; ++link) {
+ s << "\t\t" << Config::dirName(link->first.getSide()) <<
+ Config::formatInterval(link->first.getInterval()) <<
+ " = " << link->second.getName().c_str() <<
+ Config::formatInterval(link->second.getInterval()) <<
+ std::endl;
+ }
+ }
+ s << "end" << std::endl;
+
+ // aliases section (if there are any)
+ if (config.m_map.size() != config.m_nameToCanonicalName.size()) {
+ // map canonical to alias
+ typedef std::multimap<String, String,
+ CaselessCmp> CMNameMap;
+ CMNameMap aliases;
+ for (Config::NameMap::const_iterator
+ index = config.m_nameToCanonicalName.begin();
+ index != config.m_nameToCanonicalName.end();
+ ++index) {
+ if (index->first != index->second) {
+ aliases.insert(std::make_pair(index->second, index->first));
+ }
+ }
+
+ // dump it
+ String screen;
+ s << "section: aliases" << std::endl;
+ for (CMNameMap::const_iterator index = aliases.begin();
+ index != aliases.end(); ++index) {
+ if (index->first != screen) {
+ screen = index->first;
+ s << "\t" << screen.c_str() << ":" << std::endl;
+ }
+ s << "\t\t" << index->second.c_str() << std::endl;
+ }
+ s << "end" << std::endl;
+ }
+
+ // options section
+ s << "section: options" << std::endl;
+ const Config::ScreenOptions* options = config.getOptions("");
+ if (options != NULL && options->size() > 0) {
+ for (Config::ScreenOptions::const_iterator
+ option = options->begin();
+ option != options->end(); ++option) {
+ const char* name = Config::getOptionName(option->first);
+ String value = Config::getOptionValue(option->first,
+ option->second);
+ if (name != NULL && !value.empty()) {
+ s << "\t" << name << " = " << value << std::endl;
+ }
+ }
+ }
+ if (config.m_barrierAddress.isValid()) {
+ s << "\taddress = " <<
+ config.m_barrierAddress.getHostname().c_str() << std::endl;
+ }
+ s << config.m_inputFilter.format("\t");
+ s << "end" << std::endl;
+
+ return s;
+}
+
+
+//
+// ConfigReadContext
+//
+
+ConfigReadContext::ConfigReadContext(std::istream& s, SInt32 firstLine) :
+ m_stream(s),
+ m_line(firstLine - 1)
+{
+ // do nothing
+}
+
+ConfigReadContext::~ConfigReadContext()
+{
+ // do nothing
+}
+
+bool
+ConfigReadContext::readLine(String& line)
+{
+ ++m_line;
+ while (std::getline(m_stream, line)) {
+ // strip leading whitespace
+ String::size_type i = line.find_first_not_of(" \t");
+ if (i != String::npos) {
+ line.erase(0, i);
+ }
+
+ // strip comments and then trailing whitespace
+ i = line.find('#');
+ if (i != String::npos) {
+ line.erase(i);
+ }
+ i = line.find_last_not_of(" \r\t");
+ if (i != String::npos) {
+ line.erase(i + 1);
+ }
+
+ // return non empty line
+ if (!line.empty()) {
+ // make sure there are no invalid characters
+ for (i = 0; i < line.length(); ++i) {
+ if (!isgraph(line[i]) && line[i] != ' ' && line[i] != '\t') {
+ throw XConfigRead(*this,
+ "invalid character %{1}",
+ barrier::string::sprintf("%#2x", line[i]));
+ }
+ }
+
+ return true;
+ }
+
+ // next line
+ ++m_line;
+ }
+ return false;
+}
+
+UInt32
+ConfigReadContext::getLineNumber() const
+{
+ return m_line;
+}
+
+bool
+ConfigReadContext::operator!() const
+{
+ return !m_stream;
+}
+
+OptionValue
+ConfigReadContext::parseBoolean(const String& arg) const
+{
+ if (CaselessCmp::equal(arg, "true")) {
+ return static_cast<OptionValue>(true);
+ }
+ if (CaselessCmp::equal(arg, "false")) {
+ return static_cast<OptionValue>(false);
+ }
+ throw XConfigRead(*this, "invalid boolean argument \"%{1}\"", arg);
+}
+
+OptionValue
+ConfigReadContext::parseInt(const String& arg) const
+{
+ const char* s = arg.c_str();
+ char* end;
+ long tmp = strtol(s, &end, 10);
+ if (*end != '\0') {
+ // invalid characters
+ throw XConfigRead(*this, "invalid integer argument \"%{1}\"", arg);
+ }
+ OptionValue value = static_cast<OptionValue>(tmp);
+ if (value != tmp) {
+ // out of range
+ throw XConfigRead(*this, "integer argument \"%{1}\" out of range", arg);
+ }
+ return value;
+}
+
+OptionValue
+ConfigReadContext::parseModifierKey(const String& arg) const
+{
+ if (CaselessCmp::equal(arg, "shift")) {
+ return static_cast<OptionValue>(kKeyModifierIDShift);
+ }
+ if (CaselessCmp::equal(arg, "ctrl")) {
+ return static_cast<OptionValue>(kKeyModifierIDControl);
+ }
+ if (CaselessCmp::equal(arg, "alt")) {
+ return static_cast<OptionValue>(kKeyModifierIDAlt);
+ }
+ if (CaselessCmp::equal(arg, "altgr")) {
+ return static_cast<OptionValue>(kKeyModifierIDAltGr);
+ }
+ if (CaselessCmp::equal(arg, "meta")) {
+ return static_cast<OptionValue>(kKeyModifierIDMeta);
+ }
+ if (CaselessCmp::equal(arg, "super")) {
+ return static_cast<OptionValue>(kKeyModifierIDSuper);
+ }
+ if (CaselessCmp::equal(arg, "none")) {
+ return static_cast<OptionValue>(kKeyModifierIDNull);
+ }
+ throw XConfigRead(*this, "invalid argument \"%{1}\"", arg);
+}
+
+OptionValue
+ConfigReadContext::parseCorner(const String& arg) const
+{
+ if (CaselessCmp::equal(arg, "left")) {
+ return kTopLeftMask | kBottomLeftMask;
+ }
+ else if (CaselessCmp::equal(arg, "right")) {
+ return kTopRightMask | kBottomRightMask;
+ }
+ else if (CaselessCmp::equal(arg, "top")) {
+ return kTopLeftMask | kTopRightMask;
+ }
+ else if (CaselessCmp::equal(arg, "bottom")) {
+ return kBottomLeftMask | kBottomRightMask;
+ }
+ else if (CaselessCmp::equal(arg, "top-left")) {
+ return kTopLeftMask;
+ }
+ else if (CaselessCmp::equal(arg, "top-right")) {
+ return kTopRightMask;
+ }
+ else if (CaselessCmp::equal(arg, "bottom-left")) {
+ return kBottomLeftMask;
+ }
+ else if (CaselessCmp::equal(arg, "bottom-right")) {
+ return kBottomRightMask;
+ }
+ else if (CaselessCmp::equal(arg, "none")) {
+ return kNoCornerMask;
+ }
+ else if (CaselessCmp::equal(arg, "all")) {
+ return kAllCornersMask;
+ }
+ throw XConfigRead(*this, "invalid argument \"%{1}\"", arg);
+}
+
+OptionValue
+ConfigReadContext::parseCorners(const String& args) const
+{
+ // find first token
+ String::size_type i = args.find_first_not_of(" \t", 0);
+ if (i == String::npos) {
+ throw XConfigRead(*this, "missing corner argument");
+ }
+ String::size_type j = args.find_first_of(" \t", i);
+
+ // parse first corner token
+ OptionValue corners = parseCorner(args.substr(i, j - i));
+
+ // get +/-
+ i = args.find_first_not_of(" \t", j);
+ while (i != String::npos) {
+ // parse +/-
+ bool add;
+ if (args[i] == '-') {
+ add = false;
+ }
+ else if (args[i] == '+') {
+ add = true;
+ }
+ else {
+ throw XConfigRead(*this,
+ "invalid corner operator \"%{1}\"",
+ String(args.c_str() + i, 1));
+ }
+
+ // get next corner token
+ i = args.find_first_not_of(" \t", i + 1);
+ j = args.find_first_of(" \t", i);
+ if (i == String::npos) {
+ throw XConfigRead(*this, "missing corner argument");
+ }
+
+ // parse next corner token
+ if (add) {
+ corners |= parseCorner(args.substr(i, j - i));
+ }
+ else {
+ corners &= ~parseCorner(args.substr(i, j - i));
+ }
+ i = args.find_first_not_of(" \t", j);
+ }
+
+ return corners;
+}
+
+Config::Interval
+ConfigReadContext::parseInterval(const ArgList& args) const
+{
+ if (args.size() == 0) {
+ return Config::Interval(0.0f, 1.0f);
+ }
+ if (args.size() != 2 || args[0].empty() || args[1].empty()) {
+ throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
+ }
+
+ char* end;
+ double startValue = strtod(args[0].c_str(), &end);
+ if (end[0] != '\0') {
+ throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
+ }
+ double endValue = strtod(args[1].c_str(), &end);
+ if (end[0] != '\0') {
+ throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
+ }
+
+ if (startValue < 0 || startValue > 100 ||
+ endValue < 0 || endValue > 100 ||
+ startValue >= endValue) {
+ throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
+ }
+
+ return Config::Interval(startValue / 100.0f, endValue / 100.0f);
+}
+
+void
+ConfigReadContext::parseNameWithArgs(
+ const String& type, const String& line,
+ const String& delim, String::size_type& index,
+ String& name, ArgList& args) const
+{
+ // skip leading whitespace
+ String::size_type i = line.find_first_not_of(" \t", index);
+ if (i == String::npos) {
+ throw XConfigRead(*this, String("missing ") + type);
+ }
+
+ // find end of name
+ String::size_type j = line.find_first_of(" \t(" + delim, i);
+ if (j == String::npos) {
+ j = line.length();
+ }
+
+ // save name
+ name = line.substr(i, j - i);
+ args.clear();
+
+ // is it okay to not find a delimiter?
+ bool needDelim = (!delim.empty() && delim.find('\n') == String::npos);
+
+ // skip whitespace
+ i = line.find_first_not_of(" \t", j);
+ if (i == String::npos && needDelim) {
+ // expected delimiter but didn't find it
+ throw XConfigRead(*this, String("missing ") + delim[0]);
+ }
+ if (i == String::npos) {
+ // no arguments
+ index = line.length();
+ return;
+ }
+ if (line[i] != '(') {
+ // no arguments
+ index = i;
+ return;
+ }
+
+ // eat '('
+ ++i;
+
+ // parse arguments
+ j = line.find_first_of(",)", i);
+ while (j != String::npos) {
+ // extract arg
+ String arg(line.substr(i, j - i));
+ i = j;
+
+ // trim whitespace
+ j = arg.find_first_not_of(" \t");
+ if (j != String::npos) {
+ arg.erase(0, j);
+ }
+ j = arg.find_last_not_of(" \t");
+ if (j != String::npos) {
+ arg.erase(j + 1);
+ }
+
+ // save arg
+ args.push_back(arg);
+
+ // exit loop at end of arguments
+ if (line[i] == ')') {
+ break;
+ }
+
+ // eat ','
+ ++i;
+
+ // next
+ j = line.find_first_of(",)", i);
+ }
+
+ // verify ')'
+ if (j == String::npos) {
+ // expected )
+ throw XConfigRead(*this, "missing )");
+ }
+
+ // eat ')'
+ ++i;
+
+ // skip whitespace
+ j = line.find_first_not_of(" \t", i);
+ if (j == String::npos && needDelim) {
+ // expected delimiter but didn't find it
+ throw XConfigRead(*this, String("missing ") + delim[0]);
+ }
+
+ // verify delimiter
+ if (needDelim && delim.find(line[j]) == String::npos) {
+ throw XConfigRead(*this, String("expected ") + delim[0]);
+ }
+
+ if (j == String::npos) {
+ j = line.length();
+ }
+
+ index = j;
+ return;
+}
+
+IPlatformScreen::KeyInfo*
+ConfigReadContext::parseKeystroke(const String& keystroke) const
+{
+ return parseKeystroke(keystroke, std::set<String>());
+}
+
+IPlatformScreen::KeyInfo*
+ConfigReadContext::parseKeystroke(const String& keystroke,
+ const std::set<String>& screens) const
+{
+ String s = keystroke;
+
+ KeyModifierMask mask;
+ if (!barrier::KeyMap::parseModifiers(s, mask)) {
+ throw XConfigRead(*this, "unable to parse key modifiers");
+ }
+
+ KeyID key;
+ if (!barrier::KeyMap::parseKey(s, key)) {
+ throw XConfigRead(*this, "unable to parse key");
+ }
+
+ if (key == kKeyNone && mask == 0) {
+ throw XConfigRead(*this, "missing key and/or modifiers in keystroke");
+ }
+
+ return IPlatformScreen::KeyInfo::alloc(key, mask, 0, 0, screens);
+}
+
+IPlatformScreen::ButtonInfo*
+ConfigReadContext::parseMouse(const String& mouse) const
+{
+ String s = mouse;
+
+ KeyModifierMask mask;
+ if (!barrier::KeyMap::parseModifiers(s, mask)) {
+ throw XConfigRead(*this, "unable to parse button modifiers");
+ }
+
+ char* end;
+ ButtonID button = (ButtonID)strtol(s.c_str(), &end, 10);
+ if (*end != '\0') {
+ throw XConfigRead(*this, "unable to parse button");
+ }
+ if (s.empty() || button <= 0) {
+ throw XConfigRead(*this, "invalid button");
+ }
+
+ return IPlatformScreen::ButtonInfo::alloc(button, mask);
+}
+
+KeyModifierMask
+ConfigReadContext::parseModifier(const String& modifiers) const
+{
+ String s = modifiers;
+
+ KeyModifierMask mask;
+ if (!barrier::KeyMap::parseModifiers(s, mask)) {
+ throw XConfigRead(*this, "unable to parse modifiers");
+ }
+
+ if (mask == 0) {
+ throw XConfigRead(*this, "no modifiers specified");
+ }
+
+ return mask;
+}
+
+String
+ConfigReadContext::concatArgs(const ArgList& args)
+{
+ String s("(");
+ for (size_t i = 0; i < args.size(); ++i) {
+ if (i != 0) {
+ s += ",";
+ }
+ s += args[i];
+ }
+ s += ")";
+ return s;
+}
+
+
+//
+// Config I/O exceptions
+//
+
+XConfigRead::XConfigRead(const ConfigReadContext& context,
+ const String& error) :
+ m_error(barrier::string::sprintf("line %d: %s",
+ context.getLineNumber(), error.c_str()))
+{
+ // do nothing
+}
+
+XConfigRead::XConfigRead(const ConfigReadContext& context,
+ const char* errorFmt, const String& arg) :
+ m_error(barrier::string::sprintf("line %d: ", context.getLineNumber()) +
+ barrier::string::format(errorFmt, arg.c_str()))
+{
+ // do nothing
+}
+
+XConfigRead::~XConfigRead() _NOEXCEPT
+{
+ // do nothing
+}
+
+String
+XConfigRead::getWhat() const throw()
+{
+ return format("XConfigRead", "read error: %{1}", m_error.c_str());
+}
diff --git a/src/lib/server/Config.h b/src/lib/server/Config.h
new file mode 100644
index 0000000..69b01c4
--- /dev/null
+++ b/src/lib/server/Config.h
@@ -0,0 +1,549 @@
+/*
+ * barrier -- 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 "server/InputFilter.h"
+#include "barrier/option_types.h"
+#include "barrier/protocol_types.h"
+#include "barrier/IPlatformScreen.h"
+#include "net/NetworkAddress.h"
+#include "base/String.h"
+#include "base/XBase.h"
+#include "common/stdmap.h"
+#include "common/stdset.h"
+
+#include <iosfwd>
+
+class Config;
+class ConfigReadContext;
+class IEventQueue;
+
+namespace std {
+template <>
+struct iterator_traits<Config> {
+ typedef String value_type;
+ typedef ptrdiff_t difference_type;
+ typedef bidirectional_iterator_tag iterator_category;
+ typedef String* pointer;
+ typedef String& reference;
+};
+};
+
+//! Server configuration
+/*!
+This class holds server configuration information. That includes
+the names of screens and their aliases, the links between them,
+and network addresses.
+
+Note that case is preserved in screen names but is ignored when
+comparing names. Screen names and their aliases share a
+namespace and must be unique.
+*/
+class Config {
+public:
+ typedef std::map<OptionID, OptionValue> ScreenOptions;
+ typedef std::pair<float, float> Interval;
+
+ class CellEdge {
+ public:
+ CellEdge(EDirection side, float position);
+ CellEdge(EDirection side, const Interval&);
+ CellEdge(const String& name, EDirection side, const Interval&);
+ ~CellEdge();
+
+ Interval getInterval() const;
+ void setName(const String& newName);
+ String getName() const;
+ EDirection getSide() const;
+ bool overlaps(const CellEdge&) const;
+ bool isInside(float x) const;
+
+ // transform position to [0,1]
+ float transform(float x) const;
+
+ // transform [0,1] to position
+ float inverseTransform(float x) const;
+
+ // compares side and start of interval
+ bool operator<(const CellEdge&) const;
+
+ // compares side and interval
+ bool operator==(const CellEdge&) const;
+ bool operator!=(const CellEdge&) const;
+
+ private:
+ void init(const String& name, EDirection side,
+ const Interval&);
+
+ private:
+ String m_name;
+ EDirection m_side;
+ Interval m_interval;
+ };
+
+private:
+ class Name {
+ public:
+ Name(Config*, const String& name);
+
+ bool operator==(const String& name) const;
+
+ private:
+ Config* m_config;
+ String m_name;
+ };
+
+ class Cell {
+ private:
+ typedef std::map<CellEdge, CellEdge> EdgeLinks;
+
+ public:
+ typedef EdgeLinks::const_iterator const_iterator;
+
+ bool add(const CellEdge& src, const CellEdge& dst);
+ void remove(EDirection side);
+ void remove(EDirection side, float position);
+ void remove(const Name& destinationName);
+ void rename(const Name& oldName, const String& newName);
+
+ bool hasEdge(const CellEdge&) const;
+ bool overlaps(const CellEdge&) const;
+
+ bool getLink(EDirection side, float position,
+ const CellEdge*& src, const CellEdge*& dst) const;
+
+ bool operator==(const Cell&) const;
+ bool operator!=(const Cell&) const;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ private:
+ EdgeLinks m_neighbors;
+
+ public:
+ ScreenOptions m_options;
+ };
+ typedef std::map<String, Cell, barrier::string::CaselessCmp> CellMap;
+ typedef std::map<String, String, barrier::string::CaselessCmp> NameMap;
+
+public:
+ typedef Cell::const_iterator link_const_iterator;
+ typedef CellMap::const_iterator internal_const_iterator;
+ typedef NameMap::const_iterator all_const_iterator;
+ class const_iterator : std::iterator_traits<Config> {
+ public:
+ explicit const_iterator() : m_i() { }
+ explicit const_iterator(const internal_const_iterator& i) : m_i(i) { }
+
+ const_iterator& operator=(const const_iterator& i) {
+ m_i = i.m_i;
+ return *this;
+ }
+ String operator*() { return m_i->first; }
+ const String* operator->() { return &(m_i->first); }
+ const_iterator& operator++() { ++m_i; return *this; }
+ const_iterator operator++(int) { return const_iterator(m_i++); }
+ const_iterator& operator--() { --m_i; return *this; }
+ const_iterator operator--(int) { return const_iterator(m_i--); }
+ bool operator==(const const_iterator& i) const {
+ return (m_i == i.m_i);
+ }
+ bool operator!=(const const_iterator& i) const {
+ return (m_i != i.m_i);
+ }
+
+ private:
+ internal_const_iterator m_i;
+ };
+
+ Config(IEventQueue* events);
+ virtual ~Config();
+
+#ifdef TEST_ENV
+ Config() : m_inputFilter(NULL) { }
+#endif
+
+ //! @name manipulators
+ //@{
+
+ //! Add screen
+ /*!
+ Adds a screen, returning true iff successful. If a screen or
+ alias with the given name exists then it fails.
+ */
+ bool addScreen(const String& name);
+
+ //! Rename screen
+ /*!
+ Renames a screen. All references to the name are updated.
+ Returns true iff successful.
+ */
+ bool renameScreen(const String& oldName,
+ const String& newName);
+
+ //! Remove screen
+ /*!
+ Removes a screen. This also removes aliases for the screen and
+ disconnects any connections to the screen. \c name may be an
+ alias.
+ */
+ void removeScreen(const String& name);
+
+ //! Remove all screens
+ /*!
+ Removes all screens, aliases, and connections.
+ */
+ void removeAllScreens();
+
+ //! Add alias
+ /*!
+ Adds an alias for a screen name. An alias can be used
+ any place the canonical screen name can (except addScreen()).
+ Returns false if the alias name already exists or the canonical
+ name is unknown, otherwise returns true.
+ */
+ bool addAlias(const String& canonical,
+ const String& alias);
+
+ //! Remove alias
+ /*!
+ Removes an alias for a screen name. It returns false if the
+ alias is unknown or a canonical name, otherwise returns true.
+ */
+ bool removeAlias(const String& alias);
+
+ //! Remove aliases
+ /*!
+ Removes all aliases for a canonical screen name. It returns false
+ if the canonical name is unknown, otherwise returns true.
+ */
+ bool removeAliases(const String& canonical);
+
+ //! Remove all aliases
+ /*!
+ This removes all aliases but not the screens.
+ */
+ void removeAllAliases();
+
+ //! Connect screens
+ /*!
+ Establishes a one-way connection between portions of opposite edges
+ of two screens. Each portion is described by an interval defined
+ by two numbers, the start and end of the interval half-open on the
+ end. The numbers range from 0 to 1, inclusive, for the left/top
+ to the right/bottom. The user will be able to jump from the
+ \c srcStart to \c srcSend interval of \c srcSide of screen
+ \c srcName to the opposite side of screen \c dstName in the interval
+ \c dstStart and \c dstEnd when both screens are connected to the
+ server and the user isn't locked to a screen. Returns false if
+ \c srcName is unknown. \c srcStart must be less than or equal to
+ \c srcEnd and \c dstStart must be less then or equal to \c dstEnd
+ and all of \c srcStart, \c srcEnd, \c dstStart, or \c dstEnd must
+ be inside the range [0,1].
+ */
+ bool connect(const String& srcName,
+ EDirection srcSide,
+ float srcStart, float srcEnd,
+ const String& dstName,
+ float dstStart, float dstEnd);
+
+ //! Disconnect screens
+ /*!
+ Removes all connections created by connect() on side \c srcSide.
+ Returns false if \c srcName is unknown.
+ */
+ bool disconnect(const String& srcName,
+ EDirection srcSide);
+
+ //! Disconnect screens
+ /*!
+ Removes the connections created by connect() on side \c srcSide
+ covering position \c position. Returns false if \c srcName is
+ unknown.
+ */
+ bool disconnect(const String& srcName,
+ EDirection srcSide, float position);
+
+ //! Set server address
+ /*!
+ Set the barrier listen addresses. There is no default address so
+ this must be called to run a server using this configuration.
+ */
+ void setBarrierAddress(const NetworkAddress&);
+
+ //! Add a screen option
+ /*!
+ Adds an option and its value to the named screen. Replaces the
+ existing option's value if there is one. Returns true iff \c name
+ is a known screen.
+ */
+ bool addOption(const String& name,
+ OptionID option, OptionValue value);
+
+ //! Remove a screen option
+ /*!
+ Removes an option and its value from the named screen. Does
+ nothing if the option doesn't exist on the screen. Returns true
+ iff \c name is a known screen.
+ */
+ bool removeOption(const String& name, OptionID option);
+
+ //! Remove a screen options
+ /*!
+ Removes all options and values from the named screen. Returns true
+ iff \c name is a known screen.
+ */
+ bool removeOptions(const String& name);
+
+ //! Get the hot key input filter
+ /*!
+ Returns the hot key input filter. Clients can modify hotkeys using
+ that object.
+ */
+ virtual InputFilter*
+ getInputFilter();
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Test screen name validity
+ /*!
+ Returns true iff \c name is a valid screen name.
+ */
+ bool isValidScreenName(const String& name) const;
+
+ //! Get beginning (canonical) screen name iterator
+ const_iterator begin() const;
+ //! Get ending (canonical) screen name iterator
+ const_iterator end() const;
+
+ //! Get beginning screen name iterator
+ all_const_iterator beginAll() const;
+ //! Get ending screen name iterator
+ all_const_iterator endAll() const;
+
+ //! Test for screen name
+ /*!
+ Returns true iff \c name names a screen.
+ */
+ virtual bool isScreen(const String& name) const;
+
+ //! Test for canonical screen name
+ /*!
+ Returns true iff \c name is the canonical name of a screen.
+ */
+ bool isCanonicalName(const String& name) const;
+
+ //! Get canonical name
+ /*!
+ Returns the canonical name of a screen or the empty string if
+ the name is unknown. Returns the canonical name if one is given.
+ */
+ String getCanonicalName(const String& name) const;
+
+ //! Get neighbor
+ /*!
+ Returns the canonical screen name of the neighbor in the given
+ direction (set through connect()) at position \c position. Returns
+ the empty string if there is no neighbor in that direction, otherwise
+ saves the position on the neighbor in \c positionOut if it's not
+ \c NULL.
+ */
+ String getNeighbor(const String&, EDirection,
+ float position, float* positionOut) const;
+
+ //! Check for neighbor
+ /*!
+ Returns \c true if the screen has a neighbor anywhere along the edge
+ given by the direction.
+ */
+ bool hasNeighbor(const String&, EDirection) const;
+
+ //! Check for neighbor
+ /*!
+ Returns \c true if the screen has a neighbor in the given range along
+ the edge given by the direction.
+ */
+ bool hasNeighbor(const String&, EDirection,
+ float start, float end) const;
+
+ //! Get beginning neighbor iterator
+ link_const_iterator beginNeighbor(const String&) const;
+ //! Get ending neighbor iterator
+ link_const_iterator endNeighbor(const String&) const;
+
+ //! Get the server address
+ const NetworkAddress&
+ getBarrierAddress() const;
+
+ //! Get the screen options
+ /*!
+ Returns all the added options for the named screen. Returns NULL
+ if the screen is unknown and an empty collection if there are no
+ options.
+ */
+ const ScreenOptions*
+ getOptions(const String& name) const;
+
+ //! Check for lock to screen action
+ /*!
+ Returns \c true if this configuration has a lock to screen action.
+ This is for backwards compatible support of ScrollLock locking.
+ */
+ bool hasLockToScreenAction() const;
+
+ //! Compare configurations
+ bool operator==(const Config&) const;
+ //! Compare configurations
+ bool operator!=(const Config&) const;
+
+ //! Read configuration
+ /*!
+ Reads a configuration from a context. Throws XConfigRead on error
+ and context is unchanged.
+ */
+ void read(ConfigReadContext& context);
+
+ //! Read configuration
+ /*!
+ Reads a configuration from a stream. Throws XConfigRead on error.
+ */
+ friend std::istream&
+ operator>>(std::istream&, Config&);
+
+ //! Write configuration
+ /*!
+ Writes a configuration to a stream.
+ */
+ friend std::ostream&
+ operator<<(std::ostream&, const Config&);
+
+ //! Get direction name
+ /*!
+ Returns the name of a direction (for debugging).
+ */
+ static const char* dirName(EDirection);
+
+ //! Get interval as string
+ /*!
+ Returns an interval as a parseable string.
+ */
+ static String formatInterval(const Interval&);
+
+ //@}
+
+private:
+ void readSection(ConfigReadContext&);
+ void readSectionOptions(ConfigReadContext&);
+ void readSectionScreens(ConfigReadContext&);
+ void readSectionLinks(ConfigReadContext&);
+ void readSectionAliases(ConfigReadContext&);
+
+ InputFilter::Condition*
+ parseCondition(ConfigReadContext&,
+ const String& condition,
+ const std::vector<String>& args);
+ void parseAction(ConfigReadContext&,
+ const String& action,
+ const std::vector<String>& args,
+ InputFilter::Rule&, bool activate);
+
+ void parseScreens(ConfigReadContext&, const String&,
+ std::set<String>& screens) const;
+ static const char* getOptionName(OptionID);
+ static String getOptionValue(OptionID, OptionValue);
+
+private:
+ CellMap m_map;
+ NameMap m_nameToCanonicalName;
+ NetworkAddress m_barrierAddress;
+ ScreenOptions m_globalOptions;
+ InputFilter m_inputFilter;
+ bool m_hasLockToScreenAction;
+ IEventQueue* m_events;
+};
+
+//! Configuration read context
+/*!
+Maintains a context when reading a configuration from a stream.
+*/
+class ConfigReadContext {
+public:
+ typedef std::vector<String> ArgList;
+
+ ConfigReadContext(std::istream&, SInt32 firstLine = 1);
+ ~ConfigReadContext();
+
+ bool readLine(String&);
+ UInt32 getLineNumber() const;
+
+ bool operator!() const;
+
+ OptionValue parseBoolean(const String&) const;
+ OptionValue parseInt(const String&) const;
+ OptionValue parseModifierKey(const String&) const;
+ OptionValue parseCorner(const String&) const;
+ OptionValue parseCorners(const String&) const;
+ Config::Interval
+ parseInterval(const ArgList& args) const;
+ void parseNameWithArgs(
+ const String& type, const String& line,
+ const String& delim, String::size_type& index,
+ String& name, ArgList& args) const;
+ IPlatformScreen::KeyInfo*
+ parseKeystroke(const String& keystroke) const;
+ IPlatformScreen::KeyInfo*
+ parseKeystroke(const String& keystroke,
+ const std::set<String>& screens) const;
+ IPlatformScreen::ButtonInfo*
+ parseMouse(const String& mouse) const;
+ KeyModifierMask parseModifier(const String& modifiers) const;
+ std::istream& getStream() const { return m_stream; };
+
+private:
+ // not implemented
+ ConfigReadContext& operator=(const ConfigReadContext&);
+
+ static String concatArgs(const ArgList& args);
+
+private:
+ std::istream& m_stream;
+ SInt32 m_line;
+};
+
+//! Configuration stream read exception
+/*!
+Thrown when a configuration stream cannot be parsed.
+*/
+class XConfigRead : public XBase {
+public:
+ XConfigRead(const ConfigReadContext& context, const String&);
+ XConfigRead(const ConfigReadContext& context,
+ const char* errorFmt, const String& arg);
+ virtual ~XConfigRead() _NOEXCEPT;
+
+protected:
+ // XBase overrides
+ virtual String getWhat() const throw();
+
+private:
+ String m_error;
+};
diff --git a/src/lib/server/InputFilter.cpp b/src/lib/server/InputFilter.cpp
new file mode 100644
index 0000000..9e73f45
--- /dev/null
+++ b/src/lib/server/InputFilter.cpp
@@ -0,0 +1,1090 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2005 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 "server/InputFilter.h"
+#include "server/Server.h"
+#include "server/PrimaryClient.h"
+#include "barrier/KeyMap.h"
+#include "base/EventQueue.h"
+#include "base/Log.h"
+#include "base/TMethodEventJob.h"
+
+#include <cstdlib>
+#include <cstring>
+
+// -----------------------------------------------------------------------------
+// Input Filter Condition Classes
+// -----------------------------------------------------------------------------
+InputFilter::Condition::Condition()
+{
+ // do nothing
+}
+
+InputFilter::Condition::~Condition()
+{
+ // do nothing
+}
+
+void
+InputFilter::Condition::enablePrimary(PrimaryClient*)
+{
+ // do nothing
+}
+
+void
+InputFilter::Condition::disablePrimary(PrimaryClient*)
+{
+ // do nothing
+}
+
+InputFilter::KeystrokeCondition::KeystrokeCondition(
+ IEventQueue* events, IPlatformScreen::KeyInfo* info) :
+ m_id(0),
+ m_key(info->m_key),
+ m_mask(info->m_mask),
+ m_events(events)
+{
+ free(info);
+}
+
+InputFilter::KeystrokeCondition::KeystrokeCondition(
+ IEventQueue* events, KeyID key, KeyModifierMask mask) :
+ m_id(0),
+ m_key(key),
+ m_mask(mask),
+ m_events(events)
+{
+ // do nothing
+}
+
+InputFilter::KeystrokeCondition::~KeystrokeCondition()
+{
+ // do nothing
+}
+
+KeyID
+InputFilter::KeystrokeCondition::getKey() const
+{
+ return m_key;
+}
+
+KeyModifierMask
+InputFilter::KeystrokeCondition::getMask() const
+{
+ return m_mask;
+}
+
+InputFilter::Condition*
+InputFilter::KeystrokeCondition::clone() const
+{
+ return new KeystrokeCondition(m_events, m_key, m_mask);
+}
+
+String
+InputFilter::KeystrokeCondition::format() const
+{
+ return barrier::string::sprintf("keystroke(%s)",
+ barrier::KeyMap::formatKey(m_key, m_mask).c_str());
+}
+
+InputFilter::EFilterStatus
+InputFilter::KeystrokeCondition::match(const Event& event)
+{
+ EFilterStatus status;
+
+ // check for hotkey events
+ Event::Type type = event.getType();
+ if (type == m_events->forIPrimaryScreen().hotKeyDown()) {
+ status = kActivate;
+ }
+ else if (type == m_events->forIPrimaryScreen().hotKeyUp()) {
+ status = kDeactivate;
+ }
+ else {
+ return kNoMatch;
+ }
+
+ // check if it's our hotkey
+ IPrimaryScreen::HotKeyInfo* kinfo =
+ static_cast<IPlatformScreen::HotKeyInfo*>(event.getData());
+ if (kinfo->m_id != m_id) {
+ return kNoMatch;
+ }
+
+ return status;
+}
+
+void
+InputFilter::KeystrokeCondition::enablePrimary(PrimaryClient* primary)
+{
+ m_id = primary->registerHotKey(m_key, m_mask);
+}
+
+void
+InputFilter::KeystrokeCondition::disablePrimary(PrimaryClient* primary)
+{
+ primary->unregisterHotKey(m_id);
+ m_id = 0;
+}
+
+InputFilter::MouseButtonCondition::MouseButtonCondition(
+ IEventQueue* events, IPlatformScreen::ButtonInfo* info) :
+ m_button(info->m_button),
+ m_mask(info->m_mask),
+ m_events(events)
+{
+ free(info);
+}
+
+InputFilter::MouseButtonCondition::MouseButtonCondition(
+ IEventQueue* events, ButtonID button, KeyModifierMask mask) :
+ m_button(button),
+ m_mask(mask),
+ m_events(events)
+{
+ // do nothing
+}
+
+InputFilter::MouseButtonCondition::~MouseButtonCondition()
+{
+ // do nothing
+}
+
+ButtonID
+InputFilter::MouseButtonCondition::getButton() const
+{
+ return m_button;
+}
+
+KeyModifierMask
+InputFilter::MouseButtonCondition::getMask() const
+{
+ return m_mask;
+}
+
+InputFilter::Condition*
+InputFilter::MouseButtonCondition::clone() const
+{
+ return new MouseButtonCondition(m_events, m_button, m_mask);
+}
+
+String
+InputFilter::MouseButtonCondition::format() const
+{
+ String key = barrier::KeyMap::formatKey(kKeyNone, m_mask);
+ if (!key.empty()) {
+ key += "+";
+ }
+ return barrier::string::sprintf("mousebutton(%s%d)", key.c_str(), m_button);
+}
+
+InputFilter::EFilterStatus
+InputFilter::MouseButtonCondition::match(const Event& event)
+{
+ static const KeyModifierMask s_ignoreMask =
+ KeyModifierAltGr | KeyModifierCapsLock |
+ KeyModifierNumLock | KeyModifierScrollLock;
+
+ EFilterStatus status;
+
+ // check for hotkey events
+ Event::Type type = event.getType();
+ if (type == m_events->forIPrimaryScreen().buttonDown()) {
+ status = kActivate;
+ }
+ else if (type == m_events->forIPrimaryScreen().buttonUp()) {
+ status = kDeactivate;
+ }
+ else {
+ return kNoMatch;
+ }
+
+ // check if it's the right button and modifiers. ignore modifiers
+ // that cannot be combined with a mouse button.
+ IPlatformScreen::ButtonInfo* minfo =
+ static_cast<IPlatformScreen::ButtonInfo*>(event.getData());
+ if (minfo->m_button != m_button ||
+ (minfo->m_mask & ~s_ignoreMask) != m_mask) {
+ return kNoMatch;
+ }
+
+ return status;
+}
+
+InputFilter::ScreenConnectedCondition::ScreenConnectedCondition(
+ IEventQueue* events, const String& screen) :
+ m_screen(screen),
+ m_events(events)
+{
+ // do nothing
+}
+
+InputFilter::ScreenConnectedCondition::~ScreenConnectedCondition()
+{
+ // do nothing
+}
+
+InputFilter::Condition*
+InputFilter::ScreenConnectedCondition::clone() const
+{
+ return new ScreenConnectedCondition(m_events, m_screen);
+}
+
+String
+InputFilter::ScreenConnectedCondition::format() const
+{
+ return barrier::string::sprintf("connect(%s)", m_screen.c_str());
+}
+
+InputFilter::EFilterStatus
+InputFilter::ScreenConnectedCondition::match(const Event& event)
+{
+ if (event.getType() == m_events->forServer().connected()) {
+ Server::ScreenConnectedInfo* info =
+ static_cast<Server::ScreenConnectedInfo*>(event.getData());
+ if (m_screen == info->m_screen || m_screen.empty()) {
+ return kActivate;
+ }
+ }
+
+ return kNoMatch;
+}
+
+// -----------------------------------------------------------------------------
+// Input Filter Action Classes
+// -----------------------------------------------------------------------------
+InputFilter::Action::Action()
+{
+ // do nothing
+}
+
+InputFilter::Action::~Action()
+{
+ // do nothing
+}
+
+InputFilter::LockCursorToScreenAction::LockCursorToScreenAction(
+ IEventQueue* events, Mode mode) :
+ m_mode(mode),
+ m_events(events)
+{
+ // do nothing
+}
+
+InputFilter::LockCursorToScreenAction::Mode
+InputFilter::LockCursorToScreenAction::getMode() const
+{
+ return m_mode;
+}
+
+InputFilter::Action*
+InputFilter::LockCursorToScreenAction::clone() const
+{
+ return new LockCursorToScreenAction(*this);
+}
+
+String
+InputFilter::LockCursorToScreenAction::format() const
+{
+ static const char* s_mode[] = { "off", "on", "toggle" };
+
+ return barrier::string::sprintf("lockCursorToScreen(%s)", s_mode[m_mode]);
+}
+
+void
+InputFilter::LockCursorToScreenAction::perform(const Event& event)
+{
+ static const Server::LockCursorToScreenInfo::State s_state[] = {
+ Server::LockCursorToScreenInfo::kOff,
+ Server::LockCursorToScreenInfo::kOn,
+ Server::LockCursorToScreenInfo::kToggle
+ };
+
+ // send event
+ Server::LockCursorToScreenInfo* info =
+ Server::LockCursorToScreenInfo::alloc(s_state[m_mode]);
+ m_events->addEvent(Event(m_events->forServer().lockCursorToScreen(),
+ event.getTarget(), info,
+ Event::kDeliverImmediately));
+}
+
+InputFilter::SwitchToScreenAction::SwitchToScreenAction(
+ IEventQueue* events, const String& screen) :
+ m_screen(screen),
+ m_events(events)
+{
+ // do nothing
+}
+
+String
+InputFilter::SwitchToScreenAction::getScreen() const
+{
+ return m_screen;
+}
+
+InputFilter::Action*
+InputFilter::SwitchToScreenAction::clone() const
+{
+ return new SwitchToScreenAction(*this);
+}
+
+String
+InputFilter::SwitchToScreenAction::format() const
+{
+ return barrier::string::sprintf("switchToScreen(%s)", m_screen.c_str());
+}
+
+void
+InputFilter::SwitchToScreenAction::perform(const Event& event)
+{
+ // pick screen name. if m_screen is empty then use the screen from
+ // event if it has one.
+ String screen = m_screen;
+ if (screen.empty() && event.getType() == m_events->forServer().connected()) {
+ Server::ScreenConnectedInfo* info =
+ static_cast<Server::ScreenConnectedInfo*>(event.getData());
+ screen = info->m_screen;
+ }
+
+ // send event
+ Server::SwitchToScreenInfo* info =
+ Server::SwitchToScreenInfo::alloc(screen);
+ m_events->addEvent(Event(m_events->forServer().switchToScreen(),
+ event.getTarget(), info,
+ Event::kDeliverImmediately));
+}
+
+InputFilter::SwitchInDirectionAction::SwitchInDirectionAction(
+ IEventQueue* events, EDirection direction) :
+ m_direction(direction),
+ m_events(events)
+{
+ // do nothing
+}
+
+EDirection
+InputFilter::SwitchInDirectionAction::getDirection() const
+{
+ return m_direction;
+}
+
+InputFilter::Action*
+InputFilter::SwitchInDirectionAction::clone() const
+{
+ return new SwitchInDirectionAction(*this);
+}
+
+String
+InputFilter::SwitchInDirectionAction::format() const
+{
+ static const char* s_names[] = {
+ "",
+ "left",
+ "right",
+ "up",
+ "down"
+ };
+
+ return barrier::string::sprintf("switchInDirection(%s)", s_names[m_direction]);
+}
+
+void
+InputFilter::SwitchInDirectionAction::perform(const Event& event)
+{
+ Server::SwitchInDirectionInfo* info =
+ Server::SwitchInDirectionInfo::alloc(m_direction);
+ m_events->addEvent(Event(m_events->forServer().switchInDirection(),
+ event.getTarget(), info,
+ Event::kDeliverImmediately));
+}
+
+InputFilter::KeyboardBroadcastAction::KeyboardBroadcastAction(
+ IEventQueue* events, Mode mode) :
+ m_mode(mode),
+ m_events(events)
+{
+ // do nothing
+}
+
+InputFilter::KeyboardBroadcastAction::KeyboardBroadcastAction(
+ IEventQueue* events,
+ Mode mode,
+ const std::set<String>& screens) :
+ m_mode(mode),
+ m_screens(IKeyState::KeyInfo::join(screens)),
+ m_events(events)
+{
+ // do nothing
+}
+
+InputFilter::KeyboardBroadcastAction::Mode
+InputFilter::KeyboardBroadcastAction::getMode() const
+{
+ return m_mode;
+}
+
+std::set<String>
+InputFilter::KeyboardBroadcastAction::getScreens() const
+{
+ std::set<String> screens;
+ IKeyState::KeyInfo::split(m_screens.c_str(), screens);
+ return screens;
+}
+
+InputFilter::Action*
+InputFilter::KeyboardBroadcastAction::clone() const
+{
+ return new KeyboardBroadcastAction(*this);
+}
+
+String
+InputFilter::KeyboardBroadcastAction::format() const
+{
+ static const char* s_mode[] = { "off", "on", "toggle" };
+ static const char* s_name = "keyboardBroadcast";
+
+ if (m_screens.empty() || m_screens[0] == '*') {
+ return barrier::string::sprintf("%s(%s)", s_name, s_mode[m_mode]);
+ }
+ else {
+ return barrier::string::sprintf("%s(%s,%.*s)", s_name, s_mode[m_mode],
+ m_screens.size() - 2,
+ m_screens.c_str() + 1);
+ }
+}
+
+void
+InputFilter::KeyboardBroadcastAction::perform(const Event& event)
+{
+ static const Server::KeyboardBroadcastInfo::State s_state[] = {
+ Server::KeyboardBroadcastInfo::kOff,
+ Server::KeyboardBroadcastInfo::kOn,
+ Server::KeyboardBroadcastInfo::kToggle
+ };
+
+ // send event
+ Server::KeyboardBroadcastInfo* info =
+ Server::KeyboardBroadcastInfo::alloc(s_state[m_mode], m_screens);
+ m_events->addEvent(Event(m_events->forServer().keyboardBroadcast(),
+ event.getTarget(), info,
+ Event::kDeliverImmediately));
+}
+
+InputFilter::KeystrokeAction::KeystrokeAction(
+ IEventQueue* events, IPlatformScreen::KeyInfo* info, bool press) :
+ m_keyInfo(info),
+ m_press(press),
+ m_events(events)
+{
+ // do nothing
+}
+
+InputFilter::KeystrokeAction::~KeystrokeAction()
+{
+ free(m_keyInfo);
+}
+
+void
+InputFilter::KeystrokeAction::adoptInfo(IPlatformScreen::KeyInfo* info)
+{
+ free(m_keyInfo);
+ m_keyInfo = info;
+}
+
+const IPlatformScreen::KeyInfo*
+InputFilter::KeystrokeAction::getInfo() const
+{
+ return m_keyInfo;
+}
+
+bool
+InputFilter::KeystrokeAction::isOnPress() const
+{
+ return m_press;
+}
+
+InputFilter::Action*
+InputFilter::KeystrokeAction::clone() const
+{
+ IKeyState::KeyInfo* info = IKeyState::KeyInfo::alloc(*m_keyInfo);
+ return new KeystrokeAction(m_events, info, m_press);
+}
+
+String
+InputFilter::KeystrokeAction::format() const
+{
+ const char* type = formatName();
+
+ if (m_keyInfo->m_screens[0] == '\0') {
+ return barrier::string::sprintf("%s(%s)", type,
+ barrier::KeyMap::formatKey(m_keyInfo->m_key,
+ m_keyInfo->m_mask).c_str());
+ }
+ else if (m_keyInfo->m_screens[0] == '*') {
+ return barrier::string::sprintf("%s(%s,*)", type,
+ barrier::KeyMap::formatKey(m_keyInfo->m_key,
+ m_keyInfo->m_mask).c_str());
+ }
+ else {
+ return barrier::string::sprintf("%s(%s,%.*s)", type,
+ barrier::KeyMap::formatKey(m_keyInfo->m_key,
+ m_keyInfo->m_mask).c_str(),
+ strlen(m_keyInfo->m_screens + 1) - 1,
+ m_keyInfo->m_screens + 1);
+ }
+}
+
+void
+InputFilter::KeystrokeAction::perform(const Event& event)
+{
+ Event::Type type = m_press ?
+ m_events->forIKeyState().keyDown() :
+ m_events->forIKeyState().keyUp();
+
+ m_events->addEvent(Event(m_events->forIPrimaryScreen().fakeInputBegin(),
+ event.getTarget(), NULL,
+ Event::kDeliverImmediately));
+ m_events->addEvent(Event(type, event.getTarget(), m_keyInfo,
+ Event::kDeliverImmediately |
+ Event::kDontFreeData));
+ m_events->addEvent(Event(m_events->forIPrimaryScreen().fakeInputEnd(),
+ event.getTarget(), NULL,
+ Event::kDeliverImmediately));
+}
+
+const char*
+InputFilter::KeystrokeAction::formatName() const
+{
+ return (m_press ? "keyDown" : "keyUp");
+}
+
+InputFilter::MouseButtonAction::MouseButtonAction(
+ IEventQueue* events, IPlatformScreen::ButtonInfo* info, bool press) :
+ m_buttonInfo(info),
+ m_press(press),
+ m_events(events)
+{
+ // do nothing
+}
+
+InputFilter::MouseButtonAction::~MouseButtonAction()
+{
+ free(m_buttonInfo);
+}
+
+const IPlatformScreen::ButtonInfo*
+InputFilter::MouseButtonAction::getInfo() const
+{
+ return m_buttonInfo;
+}
+
+bool
+InputFilter::MouseButtonAction::isOnPress() const
+{
+ return m_press;
+}
+
+InputFilter::Action*
+InputFilter::MouseButtonAction::clone() const
+{
+ IPlatformScreen::ButtonInfo* info =
+ IPrimaryScreen::ButtonInfo::alloc(*m_buttonInfo);
+ return new MouseButtonAction(m_events, info, m_press);
+}
+
+String
+InputFilter::MouseButtonAction::format() const
+{
+ const char* type = formatName();
+
+ String key = barrier::KeyMap::formatKey(kKeyNone, m_buttonInfo->m_mask);
+ return barrier::string::sprintf("%s(%s%s%d)", type,
+ key.c_str(), key.empty() ? "" : "+",
+ m_buttonInfo->m_button);
+}
+
+void
+InputFilter::MouseButtonAction::perform(const Event& event)
+
+{
+ // send modifiers
+ IPlatformScreen::KeyInfo* modifierInfo = NULL;
+ if (m_buttonInfo->m_mask != 0) {
+ KeyID key = m_press ? kKeySetModifiers : kKeyClearModifiers;
+ modifierInfo =
+ IKeyState::KeyInfo::alloc(key, m_buttonInfo->m_mask, 0, 1);
+ m_events->addEvent(Event(m_events->forIKeyState().keyDown(),
+ event.getTarget(), modifierInfo,
+ Event::kDeliverImmediately));
+ }
+
+ // send button
+ Event::Type type = m_press ? m_events->forIPrimaryScreen().buttonDown() :
+ m_events->forIPrimaryScreen().buttonUp();
+ m_events->addEvent(Event(type, event.getTarget(), m_buttonInfo,
+ Event::kDeliverImmediately |
+ Event::kDontFreeData));
+}
+
+const char*
+InputFilter::MouseButtonAction::formatName() const
+{
+ return (m_press ? "mouseDown" : "mouseUp");
+}
+
+//
+// InputFilter::Rule
+//
+
+InputFilter::Rule::Rule() :
+ m_condition(NULL)
+{
+ // do nothing
+}
+
+InputFilter::Rule::Rule(Condition* adoptedCondition) :
+ m_condition(adoptedCondition)
+{
+ // do nothing
+}
+
+InputFilter::Rule::Rule(const Rule& rule) :
+ m_condition(NULL)
+{
+ copy(rule);
+}
+
+InputFilter::Rule::~Rule()
+{
+ clear();
+}
+
+InputFilter::Rule&
+InputFilter::Rule::operator=(const Rule& rule)
+{
+ if (&rule != this) {
+ copy(rule);
+ }
+ return *this;
+}
+
+void
+InputFilter::Rule::clear()
+{
+ delete m_condition;
+ for (ActionList::iterator i = m_activateActions.begin();
+ i != m_activateActions.end(); ++i) {
+ delete *i;
+ }
+ for (ActionList::iterator i = m_deactivateActions.begin();
+ i != m_deactivateActions.end(); ++i) {
+ delete *i;
+ }
+
+ m_condition = NULL;
+ m_activateActions.clear();
+ m_deactivateActions.clear();
+}
+
+void
+InputFilter::Rule::copy(const Rule& rule)
+{
+ clear();
+ if (rule.m_condition != NULL) {
+ m_condition = rule.m_condition->clone();
+ }
+ for (ActionList::const_iterator i = rule.m_activateActions.begin();
+ i != rule.m_activateActions.end(); ++i) {
+ m_activateActions.push_back((*i)->clone());
+ }
+ for (ActionList::const_iterator i = rule.m_deactivateActions.begin();
+ i != rule.m_deactivateActions.end(); ++i) {
+ m_deactivateActions.push_back((*i)->clone());
+ }
+}
+
+void
+InputFilter::Rule::setCondition(Condition* adopted)
+{
+ delete m_condition;
+ m_condition = adopted;
+}
+
+void
+InputFilter::Rule::adoptAction(Action* action, bool onActivation)
+{
+ if (action != NULL) {
+ if (onActivation) {
+ m_activateActions.push_back(action);
+ }
+ else {
+ m_deactivateActions.push_back(action);
+ }
+ }
+}
+
+void
+InputFilter::Rule::removeAction(bool onActivation, UInt32 index)
+{
+ if (onActivation) {
+ delete m_activateActions[index];
+ m_activateActions.erase(m_activateActions.begin() + index);
+ }
+ else {
+ delete m_deactivateActions[index];
+ m_deactivateActions.erase(m_deactivateActions.begin() + index);
+ }
+}
+
+void
+InputFilter::Rule::replaceAction(Action* adopted,
+ bool onActivation, UInt32 index)
+{
+ if (adopted == NULL) {
+ removeAction(onActivation, index);
+ }
+ else if (onActivation) {
+ delete m_activateActions[index];
+ m_activateActions[index] = adopted;
+ }
+ else {
+ delete m_deactivateActions[index];
+ m_deactivateActions[index] = adopted;
+ }
+}
+
+void
+InputFilter::Rule::enable(PrimaryClient* primaryClient)
+{
+ if (m_condition != NULL) {
+ m_condition->enablePrimary(primaryClient);
+ }
+}
+
+void
+InputFilter::Rule::disable(PrimaryClient* primaryClient)
+{
+ if (m_condition != NULL) {
+ m_condition->disablePrimary(primaryClient);
+ }
+}
+
+bool
+InputFilter::Rule::handleEvent(const Event& event)
+{
+ // NULL condition never matches
+ if (m_condition == NULL) {
+ return false;
+ }
+
+ // match
+ const ActionList* actions;
+ switch (m_condition->match(event)) {
+ default:
+ // not handled
+ return false;
+
+ case kActivate:
+ actions = &m_activateActions;
+ LOG((CLOG_DEBUG1 "activate actions"));
+ break;
+
+ case kDeactivate:
+ actions = &m_deactivateActions;
+ LOG((CLOG_DEBUG1 "deactivate actions"));
+ break;
+ }
+
+ // perform actions
+ for (ActionList::const_iterator i = actions->begin();
+ i != actions->end(); ++i) {
+ LOG((CLOG_DEBUG1 "hotkey: %s", (*i)->format().c_str()));
+ (*i)->perform(event);
+ }
+
+ return true;
+}
+
+String
+InputFilter::Rule::format() const
+{
+ String s;
+ if (m_condition != NULL) {
+ // condition
+ s += m_condition->format();
+ s += " = ";
+
+ // activate actions
+ ActionList::const_iterator i = m_activateActions.begin();
+ if (i != m_activateActions.end()) {
+ s += (*i)->format();
+ while (++i != m_activateActions.end()) {
+ s += ", ";
+ s += (*i)->format();
+ }
+ }
+
+ // deactivate actions
+ if (!m_deactivateActions.empty()) {
+ s += "; ";
+ i = m_deactivateActions.begin();
+ if (i != m_deactivateActions.end()) {
+ s += (*i)->format();
+ while (++i != m_deactivateActions.end()) {
+ s += ", ";
+ s += (*i)->format();
+ }
+ }
+ }
+ }
+ return s;
+}
+
+const InputFilter::Condition*
+InputFilter::Rule::getCondition() const
+{
+ return m_condition;
+}
+
+UInt32
+InputFilter::Rule::getNumActions(bool onActivation) const
+{
+ if (onActivation) {
+ return static_cast<UInt32>(m_activateActions.size());
+ }
+ else {
+ return static_cast<UInt32>(m_deactivateActions.size());
+ }
+}
+
+const InputFilter::Action&
+InputFilter::Rule::getAction(bool onActivation, UInt32 index) const
+{
+ if (onActivation) {
+ return *m_activateActions[index];
+ }
+ else {
+ return *m_deactivateActions[index];
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// Input Filter Class
+// -----------------------------------------------------------------------------
+InputFilter::InputFilter(IEventQueue* events) :
+ m_primaryClient(NULL),
+ m_events(events)
+{
+ // do nothing
+}
+
+InputFilter::InputFilter(const InputFilter& x) :
+ m_ruleList(x.m_ruleList),
+ m_primaryClient(NULL),
+ m_events(x.m_events)
+{
+ setPrimaryClient(x.m_primaryClient);
+}
+
+InputFilter::~InputFilter()
+{
+ setPrimaryClient(NULL);
+}
+
+InputFilter&
+InputFilter::operator=(const InputFilter& x)
+{
+ if (&x != this) {
+ PrimaryClient* oldClient = m_primaryClient;
+ setPrimaryClient(NULL);
+
+ m_ruleList = x.m_ruleList;
+
+ setPrimaryClient(oldClient);
+ }
+ return *this;
+}
+
+void
+InputFilter::addFilterRule(const Rule& rule)
+{
+ m_ruleList.push_back(rule);
+ if (m_primaryClient != NULL) {
+ m_ruleList.back().enable(m_primaryClient);
+ }
+}
+
+void
+InputFilter::removeFilterRule(UInt32 index)
+{
+ if (m_primaryClient != NULL) {
+ m_ruleList[index].disable(m_primaryClient);
+ }
+ m_ruleList.erase(m_ruleList.begin() + index);
+}
+
+InputFilter::Rule&
+InputFilter::getRule(UInt32 index)
+{
+ return m_ruleList[index];
+}
+
+void
+InputFilter::setPrimaryClient(PrimaryClient* client)
+{
+ if (m_primaryClient == client) {
+ return;
+ }
+
+ if (m_primaryClient != NULL) {
+ for (RuleList::iterator rule = m_ruleList.begin();
+ rule != m_ruleList.end(); ++rule) {
+ rule->disable(m_primaryClient);
+ }
+
+ m_events->removeHandler(m_events->forIKeyState().keyDown(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIKeyState().keyUp(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIKeyState().keyRepeat(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIPrimaryScreen().buttonDown(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIPrimaryScreen().buttonUp(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIPrimaryScreen().hotKeyDown(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIPrimaryScreen().hotKeyUp(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forServer().connected(),
+ m_primaryClient->getEventTarget());
+ }
+
+ m_primaryClient = client;
+
+ if (m_primaryClient != NULL) {
+ m_events->adoptHandler(m_events->forIKeyState().keyDown(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<InputFilter>(this,
+ &InputFilter::handleEvent));
+ m_events->adoptHandler(m_events->forIKeyState().keyUp(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<InputFilter>(this,
+ &InputFilter::handleEvent));
+ m_events->adoptHandler(m_events->forIKeyState().keyRepeat(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<InputFilter>(this,
+ &InputFilter::handleEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().buttonDown(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<InputFilter>(this,
+ &InputFilter::handleEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().buttonUp(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<InputFilter>(this,
+ &InputFilter::handleEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().hotKeyDown(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<InputFilter>(this,
+ &InputFilter::handleEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().hotKeyUp(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<InputFilter>(this,
+ &InputFilter::handleEvent));
+ m_events->adoptHandler(m_events->forServer().connected(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<InputFilter>(this,
+ &InputFilter::handleEvent));
+
+ for (RuleList::iterator rule = m_ruleList.begin();
+ rule != m_ruleList.end(); ++rule) {
+ rule->enable(m_primaryClient);
+ }
+ }
+}
+
+String
+InputFilter::format(const String& linePrefix) const
+{
+ String s;
+ for (RuleList::const_iterator i = m_ruleList.begin();
+ i != m_ruleList.end(); ++i) {
+ s += linePrefix;
+ s += i->format();
+ s += "\n";
+ }
+ return s;
+}
+
+UInt32
+InputFilter::getNumRules() const
+{
+ return static_cast<UInt32>(m_ruleList.size());
+}
+
+bool
+InputFilter::operator==(const InputFilter& x) const
+{
+ // if there are different numbers of rules then we can't be equal
+ if (m_ruleList.size() != x.m_ruleList.size()) {
+ return false;
+ }
+
+ // compare rule lists. the easiest way to do that is to format each
+ // rule into a string, sort the strings, then compare the results.
+ std::vector<String> aList, bList;
+ for (RuleList::const_iterator i = m_ruleList.begin();
+ i != m_ruleList.end(); ++i) {
+ aList.push_back(i->format());
+ }
+ for (RuleList::const_iterator i = x.m_ruleList.begin();
+ i != x.m_ruleList.end(); ++i) {
+ bList.push_back(i->format());
+ }
+ std::partial_sort(aList.begin(), aList.end(), aList.end());
+ std::partial_sort(bList.begin(), bList.end(), bList.end());
+ return (aList == bList);
+}
+
+bool
+InputFilter::operator!=(const InputFilter& x) const
+{
+ return !operator==(x);
+}
+
+void
+InputFilter::handleEvent(const Event& event, void*)
+{
+ // copy event and adjust target
+ Event myEvent(event.getType(), this, event.getData(),
+ event.getFlags() | Event::kDontFreeData |
+ Event::kDeliverImmediately);
+
+ // let each rule try to match the event until one does
+ for (RuleList::iterator rule = m_ruleList.begin();
+ rule != m_ruleList.end(); ++rule) {
+ if (rule->handleEvent(myEvent)) {
+ // handled
+ return;
+ }
+ }
+
+ // not handled so pass through
+ m_events->addEvent(myEvent);
+}
diff --git a/src/lib/server/InputFilter.h b/src/lib/server/InputFilter.h
new file mode 100644
index 0000000..73afe97
--- /dev/null
+++ b/src/lib/server/InputFilter.h
@@ -0,0 +1,361 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2005 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 "barrier/key_types.h"
+#include "barrier/mouse_types.h"
+#include "barrier/protocol_types.h"
+#include "barrier/IPlatformScreen.h"
+#include "base/String.h"
+#include "common/stdmap.h"
+#include "common/stdset.h"
+
+class PrimaryClient;
+class Event;
+class IEventQueue;
+
+class InputFilter {
+public:
+ // -------------------------------------------------------------------------
+ // Input Filter Condition Classes
+ // -------------------------------------------------------------------------
+ enum EFilterStatus {
+ kNoMatch,
+ kActivate,
+ kDeactivate
+ };
+
+ class Condition {
+ public:
+ Condition();
+ virtual ~Condition();
+
+ virtual Condition* clone() const = 0;
+ virtual String format() const = 0;
+
+ virtual EFilterStatus match(const Event&) = 0;
+
+ virtual void enablePrimary(PrimaryClient*);
+ virtual void disablePrimary(PrimaryClient*);
+ };
+
+ // KeystrokeCondition
+ class KeystrokeCondition : public Condition {
+ public:
+ KeystrokeCondition(IEventQueue* events, IPlatformScreen::KeyInfo*);
+ KeystrokeCondition(IEventQueue* events, KeyID key, KeyModifierMask mask);
+ virtual ~KeystrokeCondition();
+
+ KeyID getKey() const;
+ KeyModifierMask getMask() const;
+
+ // Condition overrides
+ virtual Condition* clone() const;
+ virtual String format() const;
+ virtual EFilterStatus match(const Event&);
+ virtual void enablePrimary(PrimaryClient*);
+ virtual void disablePrimary(PrimaryClient*);
+
+ private:
+ UInt32 m_id;
+ KeyID m_key;
+ KeyModifierMask m_mask;
+ IEventQueue* m_events;
+ };
+
+ // MouseButtonCondition
+ class MouseButtonCondition : public Condition {
+ public:
+ MouseButtonCondition(IEventQueue* events, IPlatformScreen::ButtonInfo*);
+ MouseButtonCondition(IEventQueue* events, ButtonID, KeyModifierMask mask);
+ virtual ~MouseButtonCondition();
+
+ ButtonID getButton() const;
+ KeyModifierMask getMask() const;
+
+ // Condition overrides
+ virtual Condition* clone() const;
+ virtual String format() const;
+ virtual EFilterStatus match(const Event&);
+
+ private:
+ ButtonID m_button;
+ KeyModifierMask m_mask;
+ IEventQueue* m_events;
+ };
+
+ // ScreenConnectedCondition
+ class ScreenConnectedCondition : public Condition {
+ public:
+ ScreenConnectedCondition(IEventQueue* events, const String& screen);
+ virtual ~ScreenConnectedCondition();
+
+ // Condition overrides
+ virtual Condition* clone() const;
+ virtual String format() const;
+ virtual EFilterStatus match(const Event&);
+
+ private:
+ String m_screen;
+ IEventQueue* m_events;
+ };
+
+ // -------------------------------------------------------------------------
+ // Input Filter Action Classes
+ // -------------------------------------------------------------------------
+
+ class Action {
+ public:
+ Action();
+ virtual ~Action();
+
+ virtual Action* clone() const = 0;
+ virtual String format() const = 0;
+
+ virtual void perform(const Event&) = 0;
+ };
+
+ // LockCursorToScreenAction
+ class LockCursorToScreenAction : public Action {
+ public:
+ enum Mode { kOff, kOn, kToggle };
+
+ LockCursorToScreenAction(IEventQueue* events, Mode = kToggle);
+
+ Mode getMode() const;
+
+ // Action overrides
+ virtual Action* clone() const;
+ virtual String format() const;
+ virtual void perform(const Event&);
+
+ private:
+ Mode m_mode;
+ IEventQueue* m_events;
+ };
+
+ // SwitchToScreenAction
+ class SwitchToScreenAction : public Action {
+ public:
+ SwitchToScreenAction(IEventQueue* events, const String& screen);
+
+ String getScreen() const;
+
+ // Action overrides
+ virtual Action* clone() const;
+ virtual String format() const;
+ virtual void perform(const Event&);
+
+ private:
+ String m_screen;
+ IEventQueue* m_events;
+ };
+
+ // SwitchInDirectionAction
+ class SwitchInDirectionAction : public Action {
+ public:
+ SwitchInDirectionAction(IEventQueue* events, EDirection);
+
+ EDirection getDirection() const;
+
+ // Action overrides
+ virtual Action* clone() const;
+ virtual String format() const;
+ virtual void perform(const Event&);
+
+ private:
+ EDirection m_direction;
+ IEventQueue* m_events;
+ };
+
+ // KeyboardBroadcastAction
+ class KeyboardBroadcastAction : public Action {
+ public:
+ enum Mode { kOff, kOn, kToggle };
+
+ KeyboardBroadcastAction(IEventQueue* events, Mode = kToggle);
+ KeyboardBroadcastAction(IEventQueue* events, Mode, const std::set<String>& screens);
+
+ Mode getMode() const;
+ std::set<String> getScreens() const;
+
+ // Action overrides
+ virtual Action* clone() const;
+ virtual String format() const;
+ virtual void perform(const Event&);
+
+ private:
+ Mode m_mode;
+ String m_screens;
+ IEventQueue* m_events;
+ };
+
+ // KeystrokeAction
+ class KeystrokeAction : public Action {
+ public:
+ KeystrokeAction(IEventQueue* events, IPlatformScreen::KeyInfo* adoptedInfo, bool press);
+ ~KeystrokeAction();
+
+ void adoptInfo(IPlatformScreen::KeyInfo*);
+ const IPlatformScreen::KeyInfo*
+ getInfo() const;
+ bool isOnPress() const;
+
+ // Action overrides
+ virtual Action* clone() const;
+ virtual String format() const;
+ virtual void perform(const Event&);
+
+ protected:
+ virtual const char* formatName() const;
+
+ private:
+ IPlatformScreen::KeyInfo* m_keyInfo;
+ bool m_press;
+ IEventQueue* m_events;
+ };
+
+ // MouseButtonAction -- modifier combinations not implemented yet
+ class MouseButtonAction : public Action {
+ public:
+ MouseButtonAction(IEventQueue* events,
+ IPlatformScreen::ButtonInfo* adoptedInfo,
+ bool press);
+ ~MouseButtonAction();
+
+ const IPlatformScreen::ButtonInfo*
+ getInfo() const;
+ bool isOnPress() const;
+
+ // Action overrides
+ virtual Action* clone() const;
+ virtual String format() const;
+ virtual void perform(const Event&);
+
+ protected:
+ virtual const char* formatName() const;
+
+ private:
+ IPlatformScreen::ButtonInfo* m_buttonInfo;
+ bool m_press;
+ IEventQueue* m_events;
+ };
+
+ class Rule {
+ public:
+ Rule();
+ Rule(Condition* adopted);
+ Rule(const Rule&);
+ ~Rule();
+
+ Rule& operator=(const Rule&);
+
+ // replace the condition
+ void setCondition(Condition* adopted);
+
+ // add an action to the rule
+ void adoptAction(Action*, bool onActivation);
+
+ // remove an action from the rule
+ void removeAction(bool onActivation, UInt32 index);
+
+ // replace an action in the rule
+ void replaceAction(Action* adopted,
+ bool onActivation, UInt32 index);
+
+ // enable/disable
+ void enable(PrimaryClient*);
+ void disable(PrimaryClient*);
+
+ // event handling
+ bool handleEvent(const Event&);
+
+ // convert rule to a string
+ String format() const;
+
+ // get the rule's condition
+ const Condition*
+ getCondition() const;
+
+ // get number of actions
+ UInt32 getNumActions(bool onActivation) const;
+
+ // get action by index
+ const Action& getAction(bool onActivation, UInt32 index) const;
+
+ private:
+ void clear();
+ void copy(const Rule&);
+
+ private:
+ typedef std::vector<Action*> ActionList;
+
+ Condition* m_condition;
+ ActionList m_activateActions;
+ ActionList m_deactivateActions;
+ };
+
+ // -------------------------------------------------------------------------
+ // Input Filter Class
+ // -------------------------------------------------------------------------
+ typedef std::vector<Rule> RuleList;
+
+ InputFilter(IEventQueue* events);
+ InputFilter(const InputFilter&);
+ virtual ~InputFilter();
+
+#ifdef TEST_ENV
+ InputFilter() : m_primaryClient(NULL) { }
+#endif
+
+ InputFilter& operator=(const InputFilter&);
+
+ // add rule, adopting the condition and the actions
+ void addFilterRule(const Rule& rule);
+
+ // remove a rule
+ void removeFilterRule(UInt32 index);
+
+ // get rule by index
+ Rule& getRule(UInt32 index);
+
+ // enable event filtering using the given primary client. disable
+ // if client is NULL.
+ virtual void setPrimaryClient(PrimaryClient* client);
+
+ // convert rules to a string
+ String format(const String& linePrefix) const;
+
+ // get number of rules
+ UInt32 getNumRules() const;
+
+ //! Compare filters
+ bool operator==(const InputFilter&) const;
+ //! Compare filters
+ bool operator!=(const InputFilter&) const;
+
+private:
+ // event handling
+ void handleEvent(const Event&, void*);
+
+private:
+ RuleList m_ruleList;
+ PrimaryClient* m_primaryClient;
+ IEventQueue* m_events;
+};
diff --git a/src/lib/server/PrimaryClient.cpp b/src/lib/server/PrimaryClient.cpp
new file mode 100644
index 0000000..4c9fe50
--- /dev/null
+++ b/src/lib/server/PrimaryClient.cpp
@@ -0,0 +1,274 @@
+/*
+ * barrier -- 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 "server/PrimaryClient.h"
+
+#include "barrier/Screen.h"
+#include "barrier/Clipboard.h"
+#include "base/Log.h"
+
+//
+// PrimaryClient
+//
+
+PrimaryClient::PrimaryClient(const String& name, barrier::Screen* screen) :
+ BaseClientProxy(name),
+ m_screen(screen),
+ m_fakeInputCount(0)
+{
+ // all clipboards are clean
+ for (UInt32 i = 0; i < kClipboardEnd; ++i) {
+ m_clipboardDirty[i] = false;
+ }
+}
+
+PrimaryClient::~PrimaryClient()
+{
+ // do nothing
+}
+
+void
+PrimaryClient::reconfigure(UInt32 activeSides)
+{
+ m_screen->reconfigure(activeSides);
+}
+
+UInt32
+PrimaryClient::registerHotKey(KeyID key, KeyModifierMask mask)
+{
+ return m_screen->registerHotKey(key, mask);
+}
+
+void
+PrimaryClient::unregisterHotKey(UInt32 id)
+{
+ m_screen->unregisterHotKey(id);
+}
+
+void
+PrimaryClient::fakeInputBegin()
+{
+ if (++m_fakeInputCount == 1) {
+ m_screen->fakeInputBegin();
+ }
+}
+
+void
+PrimaryClient::fakeInputEnd()
+{
+ if (--m_fakeInputCount == 0) {
+ m_screen->fakeInputEnd();
+ }
+}
+
+SInt32
+PrimaryClient::getJumpZoneSize() const
+{
+ return m_screen->getJumpZoneSize();
+}
+
+void
+PrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const
+{
+ m_screen->getCursorCenter(x, y);
+}
+
+KeyModifierMask
+PrimaryClient::getToggleMask() const
+{
+ return m_screen->pollActiveModifiers();
+}
+
+bool
+PrimaryClient::isLockedToScreen() const
+{
+ return m_screen->isLockedToScreen();
+}
+
+void*
+PrimaryClient::getEventTarget() const
+{
+ return m_screen->getEventTarget();
+}
+
+bool
+PrimaryClient::getClipboard(ClipboardID id, IClipboard* clipboard) const
+{
+ return m_screen->getClipboard(id, clipboard);
+}
+
+void
+PrimaryClient::getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const
+{
+ m_screen->getShape(x, y, width, height);
+}
+
+void
+PrimaryClient::getCursorPos(SInt32& x, SInt32& y) const
+{
+ m_screen->getCursorPos(x, y);
+}
+
+void
+PrimaryClient::enable()
+{
+ m_screen->enable();
+}
+
+void
+PrimaryClient::disable()
+{
+ m_screen->disable();
+}
+
+void
+PrimaryClient::enter(SInt32 xAbs, SInt32 yAbs,
+ UInt32 seqNum, KeyModifierMask mask, bool screensaver)
+{
+ m_screen->setSequenceNumber(seqNum);
+ if (!screensaver) {
+ m_screen->warpCursor(xAbs, yAbs);
+ }
+ m_screen->enter(mask);
+}
+
+bool
+PrimaryClient::leave()
+{
+ return m_screen->leave();
+}
+
+void
+PrimaryClient::setClipboard(ClipboardID id, const IClipboard* clipboard)
+{
+ // ignore if this clipboard is already clean
+ if (m_clipboardDirty[id]) {
+ // this clipboard is now clean
+ m_clipboardDirty[id] = false;
+
+ // set clipboard
+ m_screen->setClipboard(id, clipboard);
+ }
+}
+
+void
+PrimaryClient::grabClipboard(ClipboardID id)
+{
+ // grab clipboard
+ m_screen->grabClipboard(id);
+
+ // clipboard is dirty (because someone else owns it now)
+ m_clipboardDirty[id] = true;
+}
+
+void
+PrimaryClient::setClipboardDirty(ClipboardID id, bool dirty)
+{
+ m_clipboardDirty[id] = dirty;
+}
+
+void
+PrimaryClient::keyDown(KeyID key, KeyModifierMask mask, KeyButton button)
+{
+ if (m_fakeInputCount > 0) {
+// XXX -- don't forward keystrokes to primary screen for now
+ (void)key;
+ (void)mask;
+ (void)button;
+// m_screen->keyDown(key, mask, button);
+ }
+}
+
+void
+PrimaryClient::keyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton)
+{
+ // ignore
+}
+
+void
+PrimaryClient::keyUp(KeyID key, KeyModifierMask mask, KeyButton button)
+{
+ if (m_fakeInputCount > 0) {
+// XXX -- don't forward keystrokes to primary screen for now
+ (void)key;
+ (void)mask;
+ (void)button;
+// m_screen->keyUp(key, mask, button);
+ }
+}
+
+void
+PrimaryClient::mouseDown(ButtonID)
+{
+ // ignore
+}
+
+void
+PrimaryClient::mouseUp(ButtonID)
+{
+ // ignore
+}
+
+void
+PrimaryClient::mouseMove(SInt32 x, SInt32 y)
+{
+ m_screen->warpCursor(x, y);
+}
+
+void
+PrimaryClient::mouseRelativeMove(SInt32, SInt32)
+{
+ // ignore
+}
+
+void
+PrimaryClient::mouseWheel(SInt32, SInt32)
+{
+ // ignore
+}
+
+void
+PrimaryClient::screensaver(bool)
+{
+ // ignore
+}
+
+void
+PrimaryClient::sendDragInfo(UInt32 fileCount, const char* info, size_t size)
+{
+ // ignore
+}
+
+void
+PrimaryClient::fileChunkSending(UInt8 mark, char* data, size_t dataSize)
+{
+ // ignore
+}
+
+void
+PrimaryClient::resetOptions()
+{
+ m_screen->resetOptions();
+}
+
+void
+PrimaryClient::setOptions(const OptionsList& options)
+{
+ m_screen->setOptions(options);
+}
diff --git a/src/lib/server/PrimaryClient.h b/src/lib/server/PrimaryClient.h
new file mode 100644
index 0000000..4296aaa
--- /dev/null
+++ b/src/lib/server/PrimaryClient.h
@@ -0,0 +1,156 @@
+/*
+ * barrier -- 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 "server/BaseClientProxy.h"
+#include "barrier/protocol_types.h"
+
+namespace barrier { class Screen; }
+
+//! Primary screen as pseudo-client
+/*!
+The primary screen does not have a client associated with it. This
+class provides a pseudo-client to allow the primary screen to be
+treated as if it was a client.
+*/
+class PrimaryClient : public BaseClientProxy {
+public:
+ /*!
+ \c name is the name of the server and \p screen is primary screen.
+ */
+ PrimaryClient(const String& name, barrier::Screen* screen);
+ ~PrimaryClient();
+
+#ifdef TEST_ENV
+ PrimaryClient() : BaseClientProxy("") { }
+#endif
+
+ //! @name manipulators
+ //@{
+
+ //! Update configuration
+ /*!
+ Handles reconfiguration of jump zones.
+ */
+ virtual void reconfigure(UInt32 activeSides);
+
+ //! Register a system hotkey
+ /*!
+ Registers a system-wide hotkey for key \p key with modifiers \p mask.
+ Returns an id used to unregister the hotkey.
+ */
+ virtual UInt32 registerHotKey(KeyID key, KeyModifierMask mask);
+
+ //! Unregister a system hotkey
+ /*!
+ Unregisters a previously registered hot key.
+ */
+ virtual void unregisterHotKey(UInt32 id);
+
+ //! Prepare to synthesize input on primary screen
+ /*!
+ Prepares the primary screen to receive synthesized input. We do not
+ want to receive this synthesized input as user input so this method
+ ensures that we ignore it. Calls to \c fakeInputBegin() and
+ \c fakeInputEnd() may be nested; only the outermost have an effect.
+ */
+ void fakeInputBegin();
+
+ //! Done synthesizing input on primary screen
+ /*!
+ Undoes whatever \c fakeInputBegin() did.
+ */
+ void fakeInputEnd();
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get jump zone size
+ /*!
+ Return the jump zone size, the size of the regions on the edges of
+ the screen that cause the cursor to jump to another screen.
+ */
+ SInt32 getJumpZoneSize() const;
+
+ //! Get cursor center position
+ /*!
+ Return the cursor center position which is where we park the
+ cursor to compute cursor motion deltas and should be far from
+ the edges of the screen, typically the center.
+ */
+ void getCursorCenter(SInt32& x, SInt32& y) const;
+
+ //! Get toggle key state
+ /*!
+ Returns the primary screen's current toggle modifier key state.
+ */
+ virtual KeyModifierMask
+ getToggleMask() const;
+
+ //! Get screen lock state
+ /*!
+ Returns true if the user is locked to the screen.
+ */
+ bool isLockedToScreen() const;
+
+ //@}
+
+ // FIXME -- these probably belong on IScreen
+ virtual void enable();
+ virtual void disable();
+
+ // IScreen overrides
+ virtual void* getEventTarget() const;
+ virtual bool getClipboard(ClipboardID id, IClipboard*) const;
+ virtual void getShape(SInt32& x, SInt32& y,
+ SInt32& width, SInt32& height) const;
+ virtual void getCursorPos(SInt32& x, SInt32& y) const;
+
+ // IClient overrides
+ virtual void enter(SInt32 xAbs, SInt32 yAbs,
+ UInt32 seqNum, KeyModifierMask mask,
+ bool forScreensaver);
+ virtual bool leave();
+ virtual void setClipboard(ClipboardID, const IClipboard*);
+ virtual void grabClipboard(ClipboardID);
+ virtual void setClipboardDirty(ClipboardID, bool);
+ virtual void keyDown(KeyID, KeyModifierMask, KeyButton);
+ virtual void keyRepeat(KeyID, KeyModifierMask,
+ SInt32 count, KeyButton);
+ virtual void keyUp(KeyID, KeyModifierMask, KeyButton);
+ virtual void mouseDown(ButtonID);
+ virtual void mouseUp(ButtonID);
+ virtual void mouseMove(SInt32 xAbs, SInt32 yAbs);
+ virtual void mouseRelativeMove(SInt32 xRel, SInt32 yRel);
+ virtual void mouseWheel(SInt32 xDelta, SInt32 yDelta);
+ virtual void screensaver(bool activate);
+ virtual void resetOptions();
+ virtual void setOptions(const OptionsList& options);
+ virtual void sendDragInfo(UInt32 fileCount, const char* info, size_t size);
+ virtual void fileChunkSending(UInt8 mark, char* data, size_t dataSize);
+
+ virtual barrier::IStream*
+ getStream() const { return NULL; }
+ bool isPrimary() const { return true; }
+private:
+ barrier::Screen* m_screen;
+ bool m_clipboardDirty[kClipboardEnd];
+ SInt32 m_fakeInputCount;
+};
diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp
new file mode 100644
index 0000000..32153a6
--- /dev/null
+++ b/src/lib/server/Server.cpp
@@ -0,0 +1,2405 @@
+/*
+ * barrier -- 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 "server/Server.h"
+
+#include "server/ClientProxy.h"
+#include "server/ClientProxyUnknown.h"
+#include "server/PrimaryClient.h"
+#include "server/ClientListener.h"
+#include "barrier/FileChunk.h"
+#include "barrier/IPlatformScreen.h"
+#include "barrier/DropHelper.h"
+#include "barrier/option_types.h"
+#include "barrier/protocol_types.h"
+#include "barrier/XScreen.h"
+#include "barrier/XBarrier.h"
+#include "barrier/StreamChunker.h"
+#include "barrier/KeyState.h"
+#include "barrier/Screen.h"
+#include "barrier/PacketStreamFilter.h"
+#include "net/TCPSocket.h"
+#include "net/IDataSocket.h"
+#include "net/IListenSocket.h"
+#include "net/XSocket.h"
+#include "mt/Thread.h"
+#include "arch/Arch.h"
+#include "base/TMethodJob.h"
+#include "base/IEventQueue.h"
+#include "base/Log.h"
+#include "base/TMethodEventJob.h"
+#include "common/stdexcept.h"
+
+#include <cstring>
+#include <cstdlib>
+#include <sstream>
+#include <fstream>
+#include <ctime>
+
+//
+// Server
+//
+
+Server::Server(
+ Config& config,
+ PrimaryClient* primaryClient,
+ barrier::Screen* screen,
+ IEventQueue* events,
+ ServerArgs const& args) :
+ m_mock(false),
+ m_primaryClient(primaryClient),
+ m_active(primaryClient),
+ m_seqNum(0),
+ m_xDelta(0),
+ m_yDelta(0),
+ m_xDelta2(0),
+ m_yDelta2(0),
+ m_config(&config),
+ m_inputFilter(config.getInputFilter()),
+ m_activeSaver(NULL),
+ m_switchDir(kNoDirection),
+ m_switchScreen(NULL),
+ m_switchWaitDelay(0.0),
+ m_switchWaitTimer(NULL),
+ m_switchTwoTapDelay(0.0),
+ m_switchTwoTapEngaged(false),
+ m_switchTwoTapArmed(false),
+ m_switchTwoTapZone(3),
+ m_switchNeedsShift(false),
+ m_switchNeedsControl(false),
+ m_switchNeedsAlt(false),
+ m_relativeMoves(false),
+ m_keyboardBroadcasting(false),
+ m_lockedToScreen(false),
+ m_screen(screen),
+ m_events(events),
+ m_sendFileThread(NULL),
+ m_writeToDropDirThread(NULL),
+ m_ignoreFileTransfer(false),
+ m_enableClipboard(true),
+ m_sendDragInfoThread(NULL),
+ m_waitDragInfoThread(true),
+ m_args(args)
+{
+ // 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);
+
+ String primaryName = getName(primaryClient);
+
+ // clear clipboards
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ ClipboardInfo& clipboard = m_clipboards[id];
+ clipboard.m_clipboardOwner = primaryName;
+ clipboard.m_clipboardSeqNum = m_seqNum;
+ if (clipboard.m_clipboard.open(0)) {
+ clipboard.m_clipboard.empty();
+ clipboard.m_clipboard.close();
+ }
+ clipboard.m_clipboardData = clipboard.m_clipboard.marshall();
+ }
+
+ // install event handlers
+ m_events->adoptHandler(Event::kTimer, this,
+ new TMethodEventJob<Server>(this,
+ &Server::handleSwitchWaitTimeout));
+ m_events->adoptHandler(m_events->forIKeyState().keyDown(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleKeyDownEvent));
+ m_events->adoptHandler(m_events->forIKeyState().keyUp(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleKeyUpEvent));
+ m_events->adoptHandler(m_events->forIKeyState().keyRepeat(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleKeyRepeatEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().buttonDown(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleButtonDownEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().buttonUp(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleButtonUpEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().motionOnPrimary(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<Server>(this,
+ &Server::handleMotionPrimaryEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().motionOnSecondary(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<Server>(this,
+ &Server::handleMotionSecondaryEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().wheel(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<Server>(this,
+ &Server::handleWheelEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().screensaverActivated(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<Server>(this,
+ &Server::handleScreensaverActivatedEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().screensaverDeactivated(),
+ m_primaryClient->getEventTarget(),
+ new TMethodEventJob<Server>(this,
+ &Server::handleScreensaverDeactivatedEvent));
+ m_events->adoptHandler(m_events->forServer().switchToScreen(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleSwitchToScreenEvent));
+ m_events->adoptHandler(m_events->forServer().switchInDirection(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleSwitchInDirectionEvent));
+ m_events->adoptHandler(m_events->forServer().keyboardBroadcast(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleKeyboardBroadcastEvent));
+ m_events->adoptHandler(m_events->forServer().lockCursorToScreen(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleLockCursorToScreenEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().fakeInputBegin(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleFakeInputBeginEvent));
+ m_events->adoptHandler(m_events->forIPrimaryScreen().fakeInputEnd(),
+ m_inputFilter,
+ new TMethodEventJob<Server>(this,
+ &Server::handleFakeInputEndEvent));
+
+ if (m_args.m_enableDragDrop) {
+ m_events->adoptHandler(m_events->forFile().fileChunkSending(),
+ this,
+ new TMethodEventJob<Server>(this,
+ &Server::handleFileChunkSendingEvent));
+ m_events->adoptHandler(m_events->forFile().fileRecieveCompleted(),
+ this,
+ new TMethodEventJob<Server>(this,
+ &Server::handleFileRecieveCompletedEvent));
+ }
+
+ // add connection
+ addClient(m_primaryClient);
+
+ // set initial configuration
+ setConfig(config);
+
+ // enable primary client
+ m_primaryClient->enable();
+ m_inputFilter->setPrimaryClient(m_primaryClient);
+
+ // Determine if scroll lock is already set. If so, lock the cursor to the primary screen
+ if (m_primaryClient->getToggleMask() & KeyModifierScrollLock) {
+ LOG((CLOG_NOTE "Scroll Lock is on, locking cursor to screen"));
+ m_lockedToScreen = true;
+ }
+
+}
+
+Server::~Server()
+{
+ if (m_mock) {
+ return;
+ }
+
+ // remove event handlers and timers
+ m_events->removeHandler(m_events->forIKeyState().keyDown(),
+ m_inputFilter);
+ m_events->removeHandler(m_events->forIKeyState().keyUp(),
+ m_inputFilter);
+ m_events->removeHandler(m_events->forIKeyState().keyRepeat(),
+ m_inputFilter);
+ m_events->removeHandler(m_events->forIPrimaryScreen().buttonDown(),
+ m_inputFilter);
+ m_events->removeHandler(m_events->forIPrimaryScreen().buttonUp(),
+ m_inputFilter);
+ m_events->removeHandler(m_events->forIPrimaryScreen().motionOnPrimary(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIPrimaryScreen().motionOnSecondary(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIPrimaryScreen().wheel(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIPrimaryScreen().screensaverActivated(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIPrimaryScreen().screensaverDeactivated(),
+ m_primaryClient->getEventTarget());
+ m_events->removeHandler(m_events->forIPrimaryScreen().fakeInputBegin(),
+ m_inputFilter);
+ m_events->removeHandler(m_events->forIPrimaryScreen().fakeInputEnd(),
+ m_inputFilter);
+ m_events->removeHandler(Event::kTimer, this);
+ stopSwitch();
+
+ // force immediate disconnection of secondary clients
+ disconnect();
+ for (OldClients::iterator index = m_oldClients.begin();
+ index != m_oldClients.end(); ++index) {
+ BaseClientProxy* client = index->first;
+ m_events->deleteTimer(index->second);
+ m_events->removeHandler(Event::kTimer, client);
+ m_events->removeHandler(m_events->forClientProxy().disconnected(), client);
+ delete client;
+ }
+
+ // remove input filter
+ m_inputFilter->setPrimaryClient(NULL);
+
+ // disable and disconnect primary client
+ m_primaryClient->disable();
+ removeClient(m_primaryClient);
+}
+
+bool
+Server::setConfig(const Config& config)
+{
+ // refuse configuration if it doesn't include the primary screen
+ if (!config.isScreen(m_primaryClient->getName())) {
+ return false;
+ }
+
+ // close clients that are connected but being dropped from the
+ // configuration.
+ closeClients(config);
+
+ // cut over
+ processOptions();
+
+ // add ScrollLock as a hotkey to lock to the screen. this was a
+ // built-in feature in earlier releases and is now supported via
+ // the user configurable hotkey mechanism. if the user has already
+ // registered ScrollLock for something else then that will win but
+ // we will unfortunately generate a warning. if the user has
+ // configured a LockCursorToScreenAction then we don't add
+ // ScrollLock as a hotkey.
+ if (!m_config->hasLockToScreenAction()) {
+ IPlatformScreen::KeyInfo* key =
+ IPlatformScreen::KeyInfo::alloc(kKeyScrollLock, 0, 0, 0);
+ InputFilter::Rule rule(new InputFilter::KeystrokeCondition(m_events, key));
+ rule.adoptAction(new InputFilter::LockCursorToScreenAction(m_events), true);
+ m_inputFilter->addFilterRule(rule);
+ }
+
+ // tell primary screen about reconfiguration
+ m_primaryClient->reconfigure(getActivePrimarySides());
+
+ // tell all (connected) clients about current options
+ for (ClientList::const_iterator index = m_clients.begin();
+ index != m_clients.end(); ++index) {
+ BaseClientProxy* client = index->second;
+ sendOptions(client);
+ }
+
+ return true;
+}
+
+void
+Server::adoptClient(BaseClientProxy* client)
+{
+ assert(client != NULL);
+
+ // watch for client disconnection
+ m_events->adoptHandler(m_events->forClientProxy().disconnected(), client,
+ new TMethodEventJob<Server>(this,
+ &Server::handleClientDisconnected, client));
+
+ // name must be in our configuration
+ if (!m_config->isScreen(client->getName())) {
+ LOG((CLOG_WARN "unrecognised client name \"%s\", check server config", client->getName().c_str()));
+ closeClient(client, kMsgEUnknown);
+ return;
+ }
+
+ // add client to client list
+ if (!addClient(client)) {
+ // can only have one screen with a given name at any given time
+ LOG((CLOG_WARN "a client with name \"%s\" is already connected", getName(client).c_str()));
+ closeClient(client, kMsgEBusy);
+ return;
+ }
+ LOG((CLOG_NOTE "client \"%s\" has connected", getName(client).c_str()));
+
+ // send configuration options to client
+ sendOptions(client);
+
+ // activate screen saver on new client if active on the primary screen
+ if (m_activeSaver != NULL) {
+ client->screensaver(true);
+ }
+
+ // send notification
+ Server::ScreenConnectedInfo* info =
+ new Server::ScreenConnectedInfo(getName(client));
+ m_events->addEvent(Event(m_events->forServer().connected(),
+ m_primaryClient->getEventTarget(), info));
+}
+
+void
+Server::disconnect()
+{
+ // close all secondary clients
+ if (m_clients.size() > 1 || !m_oldClients.empty()) {
+ Config emptyConfig(m_events);
+ closeClients(emptyConfig);
+ }
+ else {
+ m_events->addEvent(Event(m_events->forServer().disconnected(), this));
+ }
+}
+
+UInt32
+Server::getNumClients() const
+{
+ return (SInt32)m_clients.size();
+}
+
+void
+Server::getClients(std::vector<String>& list) const
+{
+ list.clear();
+ for (ClientList::const_iterator index = m_clients.begin();
+ index != m_clients.end(); ++index) {
+ list.push_back(index->first);
+ }
+}
+
+String
+Server::getName(const BaseClientProxy* client) const
+{
+ String name = m_config->getCanonicalName(client->getName());
+ if (name.empty()) {
+ name = client->getName();
+ }
+ return name;
+}
+
+UInt32
+Server::getActivePrimarySides() const
+{
+ UInt32 sides = 0;
+ if (!isLockedToScreenServer()) {
+ if (hasAnyNeighbor(m_primaryClient, kLeft)) {
+ sides |= kLeftMask;
+ }
+ if (hasAnyNeighbor(m_primaryClient, kRight)) {
+ sides |= kRightMask;
+ }
+ if (hasAnyNeighbor(m_primaryClient, kTop)) {
+ sides |= kTopMask;
+ }
+ if (hasAnyNeighbor(m_primaryClient, kBottom)) {
+ sides |= kBottomMask;
+ }
+ }
+ return sides;
+}
+
+bool
+Server::isLockedToScreenServer() const
+{
+ // locked if scroll-lock is toggled on
+ return m_lockedToScreen;
+}
+
+bool
+Server::isLockedToScreen() const
+{
+ // locked if we say we're locked
+ if (isLockedToScreenServer()) {
+ return true;
+ }
+
+ // locked if primary says we're locked
+ if (m_primaryClient->isLockedToScreen()) {
+ return true;
+ }
+
+ // not locked
+ return false;
+}
+
+SInt32
+Server::getJumpZoneSize(BaseClientProxy* client) const
+{
+ if (client == m_primaryClient) {
+ return m_primaryClient->getJumpZoneSize();
+ }
+ else {
+ return 0;
+ }
+}
+
+void
+Server::switchScreen(BaseClientProxy* dst,
+ SInt32 x, SInt32 y, bool forScreensaver)
+{
+ assert(dst != NULL);
+
+#ifndef NDEBUG
+ {
+ SInt32 dx, dy, dw, dh;
+ dst->getShape(dx, dy, dw, dh);
+ assert(x >= dx && y >= dy && x < dx + dw && y < dy + dh);
+ }
+#endif
+ assert(m_active != NULL);
+
+ LOG((CLOG_INFO "switch from \"%s\" to \"%s\" at %d,%d", getName(m_active).c_str(), getName(dst).c_str(), x, y));
+
+ // stop waiting to switch
+ stopSwitch();
+
+ // record new position
+ m_x = x;
+ m_y = y;
+ m_xDelta = 0;
+ m_yDelta = 0;
+ m_xDelta2 = 0;
+ m_yDelta2 = 0;
+
+ // wrapping means leaving the active screen and entering it again.
+ // since that's a waste of time we skip that and just warp the
+ // mouse.
+ if (m_active != dst) {
+ // leave active screen
+ if (!m_active->leave()) {
+ // cannot leave screen
+ LOG((CLOG_WARN "can't leave screen"));
+ return;
+ }
+
+ // update the primary client's clipboards if we're leaving the
+ // primary screen.
+ if (m_active == m_primaryClient && m_enableClipboard) {
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ ClipboardInfo& clipboard = m_clipboards[id];
+ if (clipboard.m_clipboardOwner == getName(m_primaryClient)) {
+ onClipboardChanged(m_primaryClient,
+ id, clipboard.m_clipboardSeqNum);
+ }
+ }
+ }
+
+ // cut over
+ m_active = dst;
+
+ // increment enter sequence number
+ ++m_seqNum;
+
+ // enter new screen
+ m_active->enter(x, y, m_seqNum,
+ m_primaryClient->getToggleMask(),
+ forScreensaver);
+
+ if (m_enableClipboard) {
+ // send the clipboard data to new active screen
+ for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
+ m_active->setClipboard(id, &m_clipboards[id].m_clipboard);
+ }
+ }
+
+ Server::SwitchToScreenInfo* info =
+ Server::SwitchToScreenInfo::alloc(m_active->getName());
+ m_events->addEvent(Event(m_events->forServer().screenSwitched(), this, info));
+ }
+ else {
+ m_active->mouseMove(x, y);
+ }
+}
+
+void
+Server::jumpToScreen(BaseClientProxy* newScreen)
+{
+ assert(newScreen != NULL);
+
+ // record the current cursor position on the active screen
+ m_active->setJumpCursorPos(m_x, m_y);
+
+ // get the last cursor position on the target screen
+ SInt32 x, y;
+ newScreen->getJumpCursorPos(x, y);
+
+ switchScreen(newScreen, x, y, false);
+}
+
+float
+Server::mapToFraction(BaseClientProxy* client,
+ EDirection dir, SInt32 x, SInt32 y) const
+{
+ SInt32 sx, sy, sw, sh;
+ client->getShape(sx, sy, sw, sh);
+ switch (dir) {
+ case kLeft:
+ case kRight:
+ return static_cast<float>(y - sy + 0.5f) / static_cast<float>(sh);
+
+ case kTop:
+ case kBottom:
+ return static_cast<float>(x - sx + 0.5f) / static_cast<float>(sw);
+
+ case kNoDirection:
+ assert(0 && "bad direction");
+ break;
+ }
+ return 0.0f;
+}
+
+void
+Server::mapToPixel(BaseClientProxy* client,
+ EDirection dir, float f, SInt32& x, SInt32& y) const
+{
+ SInt32 sx, sy, sw, sh;
+ client->getShape(sx, sy, sw, sh);
+ switch (dir) {
+ case kLeft:
+ case kRight:
+ y = static_cast<SInt32>(f * sh) + sy;
+ break;
+
+ case kTop:
+ case kBottom:
+ x = static_cast<SInt32>(f * sw) + sx;
+ break;
+
+ case kNoDirection:
+ assert(0 && "bad direction");
+ break;
+ }
+}
+
+bool
+Server::hasAnyNeighbor(BaseClientProxy* client, EDirection dir) const
+{
+ assert(client != NULL);
+
+ return m_config->hasNeighbor(getName(client), dir);
+}
+
+BaseClientProxy*
+Server::getNeighbor(BaseClientProxy* src,
+ EDirection dir, SInt32& x, SInt32& y) const
+{
+ // note -- must be locked on entry
+
+ assert(src != NULL);
+
+ // get source screen name
+ String srcName = getName(src);
+ assert(!srcName.empty());
+ LOG((CLOG_DEBUG2 "find neighbor on %s of \"%s\"", Config::dirName(dir), srcName.c_str()));
+
+ // convert position to fraction
+ float t = mapToFraction(src, dir, x, y);
+
+ // search for the closest neighbor that exists in direction dir
+ float tTmp;
+ for (;;) {
+ String dstName(m_config->getNeighbor(srcName, dir, t, &tTmp));
+
+ // if nothing in that direction then return NULL. if the
+ // destination is the source then we can make no more
+ // progress in this direction. since we haven't found a
+ // connected neighbor we return NULL.
+ if (dstName.empty()) {
+ LOG((CLOG_DEBUG2 "no neighbor on %s of \"%s\"", Config::dirName(dir), srcName.c_str()));
+ return NULL;
+ }
+
+ // look up neighbor cell. if the screen is connected and
+ // ready then we can stop.
+ ClientList::const_iterator index = m_clients.find(dstName);
+ if (index != m_clients.end()) {
+ LOG((CLOG_DEBUG2 "\"%s\" is on %s of \"%s\" at %f", dstName.c_str(), Config::dirName(dir), srcName.c_str(), t));
+ mapToPixel(index->second, dir, tTmp, x, y);
+ return index->second;
+ }
+
+ // skip over unconnected screen
+ LOG((CLOG_DEBUG2 "ignored \"%s\" on %s of \"%s\"", dstName.c_str(), Config::dirName(dir), srcName.c_str()));
+ srcName = dstName;
+
+ // use position on skipped screen
+ t = tTmp;
+ }
+}
+
+BaseClientProxy*
+Server::mapToNeighbor(BaseClientProxy* src,
+ EDirection srcSide, SInt32& x, SInt32& y) const
+{
+ // note -- must be locked on entry
+
+ assert(src != NULL);
+
+ // get the first neighbor
+ BaseClientProxy* dst = getNeighbor(src, srcSide, x, y);
+ if (dst == NULL) {
+ return NULL;
+ }
+
+ // get the source screen's size
+ SInt32 dx, dy, dw, dh;
+ BaseClientProxy* lastGoodScreen = src;
+ lastGoodScreen->getShape(dx, dy, dw, dh);
+
+ // find destination screen, adjusting x or y (but not both). the
+ // searches are done in a sort of canonical screen space where
+ // the upper-left corner is 0,0 for each screen. we adjust from
+ // actual to canonical position on entry to and from canonical to
+ // actual on exit from the search.
+ switch (srcSide) {
+ case kLeft:
+ x -= dx;
+ while (dst != NULL) {
+ lastGoodScreen = dst;
+ lastGoodScreen->getShape(dx, dy, dw, dh);
+ x += dw;
+ if (x >= 0) {
+ break;
+ }
+ LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str()));
+ dst = getNeighbor(lastGoodScreen, srcSide, x, y);
+ }
+ assert(lastGoodScreen != NULL);
+ x += dx;
+ break;
+
+ case kRight:
+ x -= dx;
+ while (dst != NULL) {
+ x -= dw;
+ lastGoodScreen = dst;
+ lastGoodScreen->getShape(dx, dy, dw, dh);
+ if (x < dw) {
+ break;
+ }
+ LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str()));
+ dst = getNeighbor(lastGoodScreen, srcSide, x, y);
+ }
+ assert(lastGoodScreen != NULL);
+ x += dx;
+ break;
+
+ case kTop:
+ y -= dy;
+ while (dst != NULL) {
+ lastGoodScreen = dst;
+ lastGoodScreen->getShape(dx, dy, dw, dh);
+ y += dh;
+ if (y >= 0) {
+ break;
+ }
+ LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str()));
+ dst = getNeighbor(lastGoodScreen, srcSide, x, y);
+ }
+ assert(lastGoodScreen != NULL);
+ y += dy;
+ break;
+
+ case kBottom:
+ y -= dy;
+ while (dst != NULL) {
+ y -= dh;
+ lastGoodScreen = dst;
+ lastGoodScreen->getShape(dx, dy, dw, dh);
+ if (y < dh) {
+ break;
+ }
+ LOG((CLOG_DEBUG2 "skipping over screen %s", getName(dst).c_str()));
+ dst = getNeighbor(lastGoodScreen, srcSide, x, y);
+ }
+ assert(lastGoodScreen != NULL);
+ y += dy;
+ break;
+
+ case kNoDirection:
+ assert(0 && "bad direction");
+ return NULL;
+ }
+
+ // save destination screen
+ assert(lastGoodScreen != NULL);
+ dst = lastGoodScreen;
+
+ // if entering primary screen then be sure to move in far enough
+ // to avoid the jump zone. if entering a side that doesn't have
+ // a neighbor (i.e. an asymmetrical side) then we don't need to
+ // move inwards because that side can't provoke a jump.
+ avoidJumpZone(dst, srcSide, x, y);
+
+ return dst;
+}
+
+void
+Server::avoidJumpZone(BaseClientProxy* dst,
+ EDirection dir, SInt32& x, SInt32& y) const
+{
+ // we only need to avoid jump zones on the primary screen
+ if (dst != m_primaryClient) {
+ return;
+ }
+
+ const String dstName(getName(dst));
+ SInt32 dx, dy, dw, dh;
+ dst->getShape(dx, dy, dw, dh);
+ float t = mapToFraction(dst, dir, x, y);
+ SInt32 z = getJumpZoneSize(dst);
+
+ // move in far enough to avoid the jump zone. if entering a side
+ // that doesn't have a neighbor (i.e. an asymmetrical side) then we
+ // don't need to move inwards because that side can't provoke a jump.
+ switch (dir) {
+ case kLeft:
+ if (!m_config->getNeighbor(dstName, kRight, t, NULL).empty() &&
+ x > dx + dw - 1 - z)
+ x = dx + dw - 1 - z;
+ break;
+
+ case kRight:
+ if (!m_config->getNeighbor(dstName, kLeft, t, NULL).empty() &&
+ x < dx + z)
+ x = dx + z;
+ break;
+
+ case kTop:
+ if (!m_config->getNeighbor(dstName, kBottom, t, NULL).empty() &&
+ y > dy + dh - 1 - z)
+ y = dy + dh - 1 - z;
+ break;
+
+ case kBottom:
+ if (!m_config->getNeighbor(dstName, kTop, t, NULL).empty() &&
+ y < dy + z)
+ y = dy + z;
+ break;
+
+ case kNoDirection:
+ assert(0 && "bad direction");
+ }
+}
+
+bool
+Server::isSwitchOkay(BaseClientProxy* newScreen,
+ EDirection dir, SInt32 x, SInt32 y,
+ SInt32 xActive, SInt32 yActive)
+{
+ LOG((CLOG_DEBUG1 "try to leave \"%s\" on %s", getName(m_active).c_str(), Config::dirName(dir)));
+
+ // is there a neighbor?
+ if (newScreen == NULL) {
+ // there's no neighbor. we don't want to switch and we don't
+ // want to try to switch later.
+ LOG((CLOG_DEBUG1 "no neighbor %s", Config::dirName(dir)));
+ stopSwitch();
+ return false;
+ }
+
+ // should we switch or not?
+ bool preventSwitch = false;
+ bool allowSwitch = false;
+
+ // note if the switch direction has changed. save the new
+ // direction and screen if so.
+ bool isNewDirection = (dir != m_switchDir);
+ if (isNewDirection || m_switchScreen == NULL) {
+ m_switchDir = dir;
+ m_switchScreen = newScreen;
+ }
+
+ // is this a double tap and do we care?
+ if (!allowSwitch && m_switchTwoTapDelay > 0.0) {
+ if (isNewDirection ||
+ !isSwitchTwoTapStarted() || !shouldSwitchTwoTap()) {
+ // tapping a different or new edge or second tap not
+ // fast enough. prepare for second tap.
+ preventSwitch = true;
+ startSwitchTwoTap();
+ }
+ else {
+ // got second tap
+ allowSwitch = true;
+ }
+ }
+
+ // if waiting before a switch then prepare to switch later
+ if (!allowSwitch && m_switchWaitDelay > 0.0) {
+ if (isNewDirection || !isSwitchWaitStarted()) {
+ startSwitchWait(x, y);
+ }
+ preventSwitch = true;
+ }
+
+ // are we in a locked corner? first check if screen has the option set
+ // and, if not, check the global options.
+ const Config::ScreenOptions* options =
+ m_config->getOptions(getName(m_active));
+ if (options == NULL || options->count(kOptionScreenSwitchCorners) == 0) {
+ options = m_config->getOptions("");
+ }
+ if (options != NULL && options->count(kOptionScreenSwitchCorners) > 0) {
+ // get corner mask and size
+ Config::ScreenOptions::const_iterator i =
+ options->find(kOptionScreenSwitchCorners);
+ UInt32 corners = static_cast<UInt32>(i->second);
+ i = options->find(kOptionScreenSwitchCornerSize);
+ SInt32 size = 0;
+ if (i != options->end()) {
+ size = i->second;
+ }
+
+ // see if we're in a locked corner
+ if ((getCorner(m_active, xActive, yActive, size) & corners) != 0) {
+ // yep, no switching
+ LOG((CLOG_DEBUG1 "locked in corner"));
+ preventSwitch = true;
+ stopSwitch();
+ }
+ }
+
+ // ignore if mouse is locked to screen and don't try to switch later
+ if (!preventSwitch && isLockedToScreen()) {
+ LOG((CLOG_DEBUG1 "locked to screen"));
+ preventSwitch = true;
+ stopSwitch();
+ }
+
+ // check for optional needed modifiers
+ KeyModifierMask mods = this->m_primaryClient->getToggleMask();
+
+ if (!preventSwitch && (
+ (this->m_switchNeedsShift && ((mods & KeyModifierShift) != KeyModifierShift)) ||
+ (this->m_switchNeedsControl && ((mods & KeyModifierControl) != KeyModifierControl)) ||
+ (this->m_switchNeedsAlt && ((mods & KeyModifierAlt) != KeyModifierAlt))
+ )) {
+ LOG((CLOG_DEBUG1 "need modifiers to switch"));
+ preventSwitch = true;
+ stopSwitch();
+ }
+
+ return !preventSwitch;
+}
+
+void
+Server::noSwitch(SInt32 x, SInt32 y)
+{
+ armSwitchTwoTap(x, y);
+ stopSwitchWait();
+}
+
+void
+Server::stopSwitch()
+{
+ if (m_switchScreen != NULL) {
+ m_switchScreen = NULL;
+ m_switchDir = kNoDirection;
+ stopSwitchTwoTap();
+ stopSwitchWait();
+ }
+}
+
+void
+Server::startSwitchTwoTap()
+{
+ m_switchTwoTapEngaged = true;
+ m_switchTwoTapArmed = false;
+ m_switchTwoTapTimer.reset();
+ LOG((CLOG_DEBUG1 "waiting for second tap"));
+}
+
+void
+Server::armSwitchTwoTap(SInt32 x, SInt32 y)
+{
+ if (m_switchTwoTapEngaged) {
+ if (m_switchTwoTapTimer.getTime() > m_switchTwoTapDelay) {
+ // second tap took too long. disengage.
+ stopSwitchTwoTap();
+ }
+ else if (!m_switchTwoTapArmed) {
+ // still time for a double tap. see if we left the tap
+ // zone and, if so, arm the two tap.
+ SInt32 ax, ay, aw, ah;
+ m_active->getShape(ax, ay, aw, ah);
+ SInt32 tapZone = m_primaryClient->getJumpZoneSize();
+ if (tapZone < m_switchTwoTapZone) {
+ tapZone = m_switchTwoTapZone;
+ }
+ if (x >= ax + tapZone && x < ax + aw - tapZone &&
+ y >= ay + tapZone && y < ay + ah - tapZone) {
+ // win32 can generate bogus mouse events that appear to
+ // move in the opposite direction that the mouse actually
+ // moved. try to ignore that crap here.
+ switch (m_switchDir) {
+ case kLeft:
+ m_switchTwoTapArmed = (m_xDelta > 0 && m_xDelta2 > 0);
+ break;
+
+ case kRight:
+ m_switchTwoTapArmed = (m_xDelta < 0 && m_xDelta2 < 0);
+ break;
+
+ case kTop:
+ m_switchTwoTapArmed = (m_yDelta > 0 && m_yDelta2 > 0);
+ break;
+
+ case kBottom:
+ m_switchTwoTapArmed = (m_yDelta < 0 && m_yDelta2 < 0);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+
+void
+Server::stopSwitchTwoTap()
+{
+ m_switchTwoTapEngaged = false;
+ m_switchTwoTapArmed = false;
+}
+
+bool
+Server::isSwitchTwoTapStarted() const
+{
+ return m_switchTwoTapEngaged;
+}
+
+bool
+Server::shouldSwitchTwoTap() const
+{
+ // this is the second tap if two-tap is armed and this tap
+ // came fast enough
+ return (m_switchTwoTapArmed &&
+ m_switchTwoTapTimer.getTime() <= m_switchTwoTapDelay);
+}
+
+void
+Server::startSwitchWait(SInt32 x, SInt32 y)
+{
+ stopSwitchWait();
+ m_switchWaitX = x;
+ m_switchWaitY = y;
+ m_switchWaitTimer = m_events->newOneShotTimer(m_switchWaitDelay, this);
+ LOG((CLOG_DEBUG1 "waiting to switch"));
+}
+
+void
+Server::stopSwitchWait()
+{
+ if (m_switchWaitTimer != NULL) {
+ m_events->deleteTimer(m_switchWaitTimer);
+ m_switchWaitTimer = NULL;
+ }
+}
+
+bool
+Server::isSwitchWaitStarted() const
+{
+ return (m_switchWaitTimer != NULL);
+}
+
+UInt32
+Server::getCorner(BaseClientProxy* client,
+ SInt32 x, SInt32 y, SInt32 size) const
+{
+ assert(client != NULL);
+
+ // get client screen shape
+ SInt32 ax, ay, aw, ah;
+ client->getShape(ax, ay, aw, ah);
+
+ // check for x,y on the left or right
+ SInt32 xSide;
+ if (x <= ax) {
+ xSide = -1;
+ }
+ else if (x >= ax + aw - 1) {
+ xSide = 1;
+ }
+ else {
+ xSide = 0;
+ }
+
+ // check for x,y on the top or bottom
+ SInt32 ySide;
+ if (y <= ay) {
+ ySide = -1;
+ }
+ else if (y >= ay + ah - 1) {
+ ySide = 1;
+ }
+ else {
+ ySide = 0;
+ }
+
+ // if against the left or right then check if y is within size
+ if (xSide != 0) {
+ if (y < ay + size) {
+ return (xSide < 0) ? kTopLeftMask : kTopRightMask;
+ }
+ else if (y >= ay + ah - size) {
+ return (xSide < 0) ? kBottomLeftMask : kBottomRightMask;
+ }
+ }
+
+ // if against the left or right then check if y is within size
+ if (ySide != 0) {
+ if (x < ax + size) {
+ return (ySide < 0) ? kTopLeftMask : kBottomLeftMask;
+ }
+ else if (x >= ax + aw - size) {
+ return (ySide < 0) ? kTopRightMask : kBottomRightMask;
+ }
+ }
+
+ return kNoCornerMask;
+}
+
+void
+Server::stopRelativeMoves()
+{
+ if (m_relativeMoves && m_active != m_primaryClient) {
+ // warp to the center of the active client so we know where we are
+ SInt32 ax, ay, aw, ah;
+ m_active->getShape(ax, ay, aw, ah);
+ m_x = ax + (aw >> 1);
+ m_y = ay + (ah >> 1);
+ m_xDelta = 0;
+ m_yDelta = 0;
+ m_xDelta2 = 0;
+ m_yDelta2 = 0;
+ LOG((CLOG_DEBUG2 "synchronize move on %s by %d,%d", getName(m_active).c_str(), m_x, m_y));
+ m_active->mouseMove(m_x, m_y);
+ }
+}
+
+void
+Server::sendOptions(BaseClientProxy* client) const
+{
+ OptionsList optionsList;
+
+ // look up options for client
+ const Config::ScreenOptions* options =
+ m_config->getOptions(getName(client));
+ if (options != NULL) {
+ // convert options to a more convenient form for sending
+ optionsList.reserve(2 * options->size());
+ for (Config::ScreenOptions::const_iterator index = options->begin();
+ index != options->end(); ++index) {
+ optionsList.push_back(index->first);
+ optionsList.push_back(static_cast<UInt32>(index->second));
+ }
+ }
+
+ // look up global options
+ options = m_config->getOptions("");
+ if (options != NULL) {
+ // convert options to a more convenient form for sending
+ optionsList.reserve(optionsList.size() + 2 * options->size());
+ for (Config::ScreenOptions::const_iterator index = options->begin();
+ index != options->end(); ++index) {
+ optionsList.push_back(index->first);
+ optionsList.push_back(static_cast<UInt32>(index->second));
+ }
+ }
+
+ // send the options
+ client->resetOptions();
+ client->setOptions(optionsList);
+}
+
+void
+Server::processOptions()
+{
+ const Config::ScreenOptions* options = m_config->getOptions("");
+ if (options == NULL) {
+ return;
+ }
+
+ m_switchNeedsShift = false; // it seems if i don't add these
+ m_switchNeedsControl = false; // lines, the 'reload config' option
+ m_switchNeedsAlt = false; // doesnt' work correct.
+
+ bool newRelativeMoves = m_relativeMoves;
+ for (Config::ScreenOptions::const_iterator index = options->begin();
+ index != options->end(); ++index) {
+ const OptionID id = index->first;
+ const OptionValue value = index->second;
+ if (id == kOptionScreenSwitchDelay) {
+ m_switchWaitDelay = 1.0e-3 * static_cast<double>(value);
+ if (m_switchWaitDelay < 0.0) {
+ m_switchWaitDelay = 0.0;
+ }
+ stopSwitchWait();
+ }
+ else if (id == kOptionScreenSwitchTwoTap) {
+ m_switchTwoTapDelay = 1.0e-3 * static_cast<double>(value);
+ if (m_switchTwoTapDelay < 0.0) {
+ m_switchTwoTapDelay = 0.0;
+ }
+ stopSwitchTwoTap();
+ }
+ else if (id == kOptionScreenSwitchNeedsControl) {
+ m_switchNeedsControl = (value != 0);
+ }
+ else if (id == kOptionScreenSwitchNeedsShift) {
+ m_switchNeedsShift = (value != 0);
+ }
+ else if (id == kOptionScreenSwitchNeedsAlt) {
+ m_switchNeedsAlt = (value != 0);
+ }
+ else if (id == kOptionRelativeMouseMoves) {
+ newRelativeMoves = (value != 0);
+ }
+ else if (id == kOptionClipboardSharing) {
+ m_enableClipboard = (value != 0);
+
+ if (m_enableClipboard == false) {
+ LOG((CLOG_NOTE "clipboard sharing is disabled"));
+ }
+ }
+ }
+ if (m_relativeMoves && !newRelativeMoves) {
+ stopRelativeMoves();
+ }
+ m_relativeMoves = newRelativeMoves;
+}
+
+void
+Server::handleShapeChanged(const Event&, void* vclient)
+{
+ // ignore events from unknown clients
+ BaseClientProxy* client = static_cast<BaseClientProxy*>(vclient);
+ if (m_clientSet.count(client) == 0) {
+ return;
+ }
+
+ LOG((CLOG_DEBUG "screen \"%s\" shape changed", getName(client).c_str()));
+
+ // update jump coordinate
+ SInt32 x, y;
+ client->getCursorPos(x, y);
+ client->setJumpCursorPos(x, y);
+
+ // update the mouse coordinates
+ if (client == m_active) {
+ m_x = x;
+ m_y = y;
+ }
+
+ // handle resolution change to primary screen
+ if (client == m_primaryClient) {
+ if (client == m_active) {
+ onMouseMovePrimary(m_x, m_y);
+ }
+ else {
+ onMouseMoveSecondary(0, 0);
+ }
+ }
+}
+
+void
+Server::handleClipboardGrabbed(const Event& event, void* vclient)
+{
+ if (!m_enableClipboard) {
+ return;
+ }
+
+ // ignore events from unknown clients
+ BaseClientProxy* grabber = static_cast<BaseClientProxy*>(vclient);
+ if (m_clientSet.count(grabber) == 0) {
+ return;
+ }
+ const IScreen::ClipboardInfo* info =
+ static_cast<const IScreen::ClipboardInfo*>(event.getData());
+
+ // ignore grab if sequence number is old. always allow primary
+ // screen to grab.
+ ClipboardInfo& clipboard = m_clipboards[info->m_id];
+ if (grabber != m_primaryClient &&
+ info->m_sequenceNumber < clipboard.m_clipboardSeqNum) {
+ LOG((CLOG_INFO "ignored screen \"%s\" grab of clipboard %d", getName(grabber).c_str(), info->m_id));
+ return;
+ }
+
+ // mark screen as owning clipboard
+ LOG((CLOG_INFO "screen \"%s\" grabbed clipboard %d from \"%s\"", getName(grabber).c_str(), info->m_id, clipboard.m_clipboardOwner.c_str()));
+ clipboard.m_clipboardOwner = getName(grabber);
+ clipboard.m_clipboardSeqNum = info->m_sequenceNumber;
+
+ // clear the clipboard data (since it's not known at this point)
+ if (clipboard.m_clipboard.open(0)) {
+ clipboard.m_clipboard.empty();
+ clipboard.m_clipboard.close();
+ }
+ clipboard.m_clipboardData = clipboard.m_clipboard.marshall();
+
+ // tell all other screens to take ownership of clipboard. tell the
+ // grabber that it's clipboard isn't dirty.
+ for (ClientList::iterator index = m_clients.begin();
+ index != m_clients.end(); ++index) {
+ BaseClientProxy* client = index->second;
+ if (client == grabber) {
+ client->setClipboardDirty(info->m_id, false);
+ }
+ else {
+ client->grabClipboard(info->m_id);
+ }
+ }
+}
+
+void
+Server::handleClipboardChanged(const Event& event, void* vclient)
+{
+ // ignore events from unknown clients
+ BaseClientProxy* sender = static_cast<BaseClientProxy*>(vclient);
+ if (m_clientSet.count(sender) == 0) {
+ return;
+ }
+ const IScreen::ClipboardInfo* info =
+ static_cast<const IScreen::ClipboardInfo*>(event.getData());
+ onClipboardChanged(sender, info->m_id, info->m_sequenceNumber);
+}
+
+void
+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);
+}
+
+void
+Server::handleKeyUpEvent(const Event& event, void*)
+{
+ IPlatformScreen::KeyInfo* info =
+ static_cast<IPlatformScreen::KeyInfo*>(event.getData());
+ onKeyUp(info->m_key, info->m_mask, info->m_button, info->m_screens);
+}
+
+void
+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);
+}
+
+void
+Server::handleButtonDownEvent(const Event& event, void*)
+{
+ IPlatformScreen::ButtonInfo* info =
+ static_cast<IPlatformScreen::ButtonInfo*>(event.getData());
+ onMouseDown(info->m_button);
+}
+
+void
+Server::handleButtonUpEvent(const Event& event, void*)
+{
+ IPlatformScreen::ButtonInfo* info =
+ static_cast<IPlatformScreen::ButtonInfo*>(event.getData());
+ onMouseUp(info->m_button);
+}
+
+void
+Server::handleMotionPrimaryEvent(const Event& event, void*)
+{
+ IPlatformScreen::MotionInfo* info =
+ static_cast<IPlatformScreen::MotionInfo*>(event.getData());
+ onMouseMovePrimary(info->m_x, info->m_y);
+}
+
+void
+Server::handleMotionSecondaryEvent(const Event& event, void*)
+{
+ IPlatformScreen::MotionInfo* info =
+ static_cast<IPlatformScreen::MotionInfo*>(event.getData());
+ onMouseMoveSecondary(info->m_x, info->m_y);
+}
+
+void
+Server::handleWheelEvent(const Event& event, void*)
+{
+ IPlatformScreen::WheelInfo* info =
+ static_cast<IPlatformScreen::WheelInfo*>(event.getData());
+ onMouseWheel(info->m_xDelta, info->m_yDelta);
+}
+
+void
+Server::handleScreensaverActivatedEvent(const Event&, void*)
+{
+ onScreensaver(true);
+}
+
+void
+Server::handleScreensaverDeactivatedEvent(const Event&, void*)
+{
+ onScreensaver(false);
+}
+
+void
+Server::handleSwitchWaitTimeout(const Event&, void*)
+{
+ // ignore if mouse is locked to screen
+ if (isLockedToScreen()) {
+ LOG((CLOG_DEBUG1 "locked to screen"));
+ stopSwitch();
+ return;
+ }
+
+ // switch screen
+ switchScreen(m_switchScreen, m_switchWaitX, m_switchWaitY, false);
+}
+
+void
+Server::handleClientDisconnected(const Event&, void* vclient)
+{
+ // client has disconnected. it might be an old client or an
+ // active client. we don't care so just handle it both ways.
+ BaseClientProxy* client = static_cast<BaseClientProxy*>(vclient);
+ removeActiveClient(client);
+ removeOldClient(client);
+
+ delete client;
+}
+
+void
+Server::handleClientCloseTimeout(const Event&, void* vclient)
+{
+ // client took too long to disconnect. just dump it.
+ BaseClientProxy* client = static_cast<BaseClientProxy*>(vclient);
+ LOG((CLOG_NOTE "forced disconnection of client \"%s\"", getName(client).c_str()));
+ removeOldClient(client);
+
+ delete client;
+}
+
+void
+Server::handleSwitchToScreenEvent(const Event& event, void*)
+{
+ SwitchToScreenInfo* info =
+ static_cast<SwitchToScreenInfo*>(event.getData());
+
+ ClientList::const_iterator index = m_clients.find(info->m_screen);
+ if (index == m_clients.end()) {
+ LOG((CLOG_DEBUG1 "screen \"%s\" not active", info->m_screen));
+ }
+ else {
+ jumpToScreen(index->second);
+ }
+}
+
+void
+Server::handleSwitchInDirectionEvent(const Event& event, void*)
+{
+ SwitchInDirectionInfo* info =
+ static_cast<SwitchInDirectionInfo*>(event.getData());
+
+ // jump to screen in chosen direction from center of this screen
+ SInt32 x = m_x, y = m_y;
+ BaseClientProxy* newScreen =
+ getNeighbor(m_active, info->m_direction, x, y);
+ if (newScreen == NULL) {
+ LOG((CLOG_DEBUG1 "no neighbor %s", Config::dirName(info->m_direction)));
+ }
+ else {
+ jumpToScreen(newScreen);
+ }
+}
+
+void
+Server::handleKeyboardBroadcastEvent(const Event& event, void*)
+{
+ KeyboardBroadcastInfo* info = (KeyboardBroadcastInfo*)event.getData();
+
+ // choose new state
+ bool newState;
+ switch (info->m_state) {
+ case KeyboardBroadcastInfo::kOff:
+ newState = false;
+ break;
+
+ default:
+ case KeyboardBroadcastInfo::kOn:
+ newState = true;
+ break;
+
+ case KeyboardBroadcastInfo::kToggle:
+ newState = !m_keyboardBroadcasting;
+ break;
+ }
+
+ // enter new state
+ if (newState != m_keyboardBroadcasting ||
+ info->m_screens != m_keyboardBroadcastingScreens) {
+ m_keyboardBroadcasting = newState;
+ m_keyboardBroadcastingScreens = info->m_screens;
+ LOG((CLOG_DEBUG "keyboard broadcasting %s: %s", m_keyboardBroadcasting ? "on" : "off", m_keyboardBroadcastingScreens.c_str()));
+ }
+}
+
+void
+Server::handleLockCursorToScreenEvent(const Event& event, void*)
+{
+ LockCursorToScreenInfo* info = (LockCursorToScreenInfo*)event.getData();
+
+ // choose new state
+ bool newState;
+ switch (info->m_state) {
+ case LockCursorToScreenInfo::kOff:
+ newState = false;
+ break;
+
+ default:
+ case LockCursorToScreenInfo::kOn:
+ newState = true;
+ break;
+
+ case LockCursorToScreenInfo::kToggle:
+ newState = !m_lockedToScreen;
+ break;
+ }
+
+ // enter new state
+ if (newState != m_lockedToScreen) {
+ m_lockedToScreen = newState;
+ LOG((CLOG_NOTE "cursor %s current screen", m_lockedToScreen ? "locked to" : "unlocked from"));
+
+ m_primaryClient->reconfigure(getActivePrimarySides());
+ if (!isLockedToScreenServer()) {
+ stopRelativeMoves();
+ }
+ }
+}
+
+void
+Server::handleFakeInputBeginEvent(const Event&, void*)
+{
+ m_primaryClient->fakeInputBegin();
+}
+
+void
+Server::handleFakeInputEndEvent(const Event&, void*)
+{
+ m_primaryClient->fakeInputEnd();
+}
+
+void
+Server::handleFileChunkSendingEvent(const Event& event, void*)
+{
+ onFileChunkSending(event.getData());
+}
+
+void
+Server::handleFileRecieveCompletedEvent(const Event& event, void*)
+{
+ onFileRecieveCompleted();
+}
+
+void
+Server::onClipboardChanged(BaseClientProxy* sender,
+ ClipboardID id, UInt32 seqNum)
+{
+ ClipboardInfo& clipboard = m_clipboards[id];
+
+ // ignore update if sequence number is old
+ if (seqNum < clipboard.m_clipboardSeqNum) {
+ LOG((CLOG_INFO "ignored screen \"%s\" update of clipboard %d (missequenced)", getName(sender).c_str(), id));
+ return;
+ }
+
+ // should be the expected client
+ assert(sender == m_clients.find(clipboard.m_clipboardOwner)->second);
+
+ // get data
+ sender->getClipboard(id, &clipboard.m_clipboard);
+
+ // ignore if data hasn't changed
+ String data = clipboard.m_clipboard.marshall();
+ if (data == clipboard.m_clipboardData) {
+ LOG((CLOG_DEBUG "ignored screen \"%s\" update of clipboard %d (unchanged)", clipboard.m_clipboardOwner.c_str(), id));
+ return;
+ }
+
+ // got new data
+ LOG((CLOG_INFO "screen \"%s\" updated clipboard %d", clipboard.m_clipboardOwner.c_str(), id));
+ clipboard.m_clipboardData = data;
+
+ // tell all clients except the sender that the clipboard is dirty
+ for (ClientList::const_iterator index = m_clients.begin();
+ index != m_clients.end(); ++index) {
+ BaseClientProxy* client = index->second;
+ client->setClipboardDirty(id, client != sender);
+ }
+
+ // send the new clipboard to the active screen
+ m_active->setClipboard(id, &clipboard.m_clipboard);
+}
+
+void
+Server::onScreensaver(bool activated)
+{
+ LOG((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated"));
+
+ if (activated) {
+ // save current screen and position
+ m_activeSaver = m_active;
+ m_xSaver = m_x;
+ m_ySaver = m_y;
+
+ // jump to primary screen
+ if (m_active != m_primaryClient) {
+ switchScreen(m_primaryClient, 0, 0, true);
+ }
+ }
+ else {
+ // jump back to previous screen and position. we must check
+ // that the position is still valid since the screen may have
+ // changed resolutions while the screen saver was running.
+ if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) {
+ // check position
+ BaseClientProxy* screen = m_activeSaver;
+ SInt32 x, y, w, h;
+ screen->getShape(x, y, w, h);
+ SInt32 zoneSize = getJumpZoneSize(screen);
+ if (m_xSaver < x + zoneSize) {
+ m_xSaver = x + zoneSize;
+ }
+ else if (m_xSaver >= x + w - zoneSize) {
+ m_xSaver = x + w - zoneSize - 1;
+ }
+ if (m_ySaver < y + zoneSize) {
+ m_ySaver = y + zoneSize;
+ }
+ else if (m_ySaver >= y + h - zoneSize) {
+ m_ySaver = y + h - zoneSize - 1;
+ }
+
+ // jump
+ switchScreen(screen, m_xSaver, m_ySaver, false);
+ }
+
+ // reset state
+ m_activeSaver = NULL;
+ }
+
+ // send message to all clients
+ for (ClientList::const_iterator index = m_clients.begin();
+ index != m_clients.end(); ++index) {
+ BaseClientProxy* client = index->second;
+ client->screensaver(activated);
+ }
+}
+
+void
+Server::onKeyDown(KeyID id, KeyModifierMask mask, KeyButton button,
+ const char* screens)
+{
+ LOG((CLOG_DEBUG1 "onKeyDown id=%d mask=0x%04x button=0x%04x", id, mask, button));
+ assert(m_active != NULL);
+
+ // relay
+ if (!m_keyboardBroadcasting && IKeyState::KeyInfo::isDefault(screens)) {
+ m_active->keyDown(id, mask, button);
+ }
+ else {
+ if (!screens && m_keyboardBroadcasting) {
+ screens = m_keyboardBroadcastingScreens.c_str();
+ if (IKeyState::KeyInfo::isDefault(screens)) {
+ screens = "*";
+ }
+ }
+ 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);
+ }
+ }
+ }
+}
+
+void
+Server::onKeyUp(KeyID id, KeyModifierMask mask, KeyButton button,
+ const char* screens)
+{
+ LOG((CLOG_DEBUG1 "onKeyUp id=%d mask=0x%04x button=0x%04x", id, mask, button));
+ assert(m_active != NULL);
+
+ // relay
+ if (!m_keyboardBroadcasting && IKeyState::KeyInfo::isDefault(screens)) {
+ m_active->keyUp(id, mask, button);
+ }
+ else {
+ if (!screens && m_keyboardBroadcasting) {
+ screens = m_keyboardBroadcastingScreens.c_str();
+ if (IKeyState::KeyInfo::isDefault(screens)) {
+ screens = "*";
+ }
+ }
+ for (ClientList::const_iterator index = m_clients.begin();
+ index != m_clients.end(); ++index) {
+ if (IKeyState::KeyInfo::contains(screens, index->first)) {
+ index->second->keyUp(id, mask, button);
+ }
+ }
+ }
+}
+
+void
+Server::onKeyRepeat(KeyID id, KeyModifierMask mask,
+ SInt32 count, KeyButton button)
+{
+ LOG((CLOG_DEBUG1 "onKeyRepeat id=%d mask=0x%04x count=%d button=0x%04x", id, mask, count, button));
+ assert(m_active != NULL);
+
+ // relay
+ m_active->keyRepeat(id, mask, count, button);
+}
+
+void
+Server::onMouseDown(ButtonID id)
+{
+ LOG((CLOG_DEBUG1 "onMouseDown id=%d", id));
+ assert(m_active != NULL);
+
+ // relay
+ m_active->mouseDown(id);
+
+ // reset this variable back to default value true
+ m_waitDragInfoThread = true;
+}
+
+void
+Server::onMouseUp(ButtonID id)
+{
+ LOG((CLOG_DEBUG1 "onMouseUp id=%d", id));
+ assert(m_active != NULL);
+
+ // relay
+ m_active->mouseUp(id);
+
+ if (m_ignoreFileTransfer) {
+ m_ignoreFileTransfer = false;
+ return;
+ }
+
+ if (m_args.m_enableDragDrop) {
+ if (!m_screen->isOnScreen()) {
+ String& file = m_screen->getDraggingFilename();
+ if (!file.empty()) {
+ sendFileToClient(file.c_str());
+ }
+ }
+
+ // always clear dragging filename
+ m_screen->clearDraggingFilename();
+ }
+}
+
+bool
+Server::onMouseMovePrimary(SInt32 x, SInt32 y)
+{
+ LOG((CLOG_DEBUG4 "onMouseMovePrimary %d,%d", x, y));
+
+ // mouse move on primary (server's) screen
+ if (m_active != m_primaryClient) {
+ // stale event -- we're actually on a secondary screen
+ return false;
+ }
+
+ // save last delta
+ m_xDelta2 = m_xDelta;
+ m_yDelta2 = m_yDelta;
+
+ // save current delta
+ m_xDelta = x - m_x;
+ m_yDelta = y - m_y;
+
+ // save position
+ m_x = x;
+ m_y = y;
+
+ // get screen shape
+ SInt32 ax, ay, aw, ah;
+ m_active->getShape(ax, ay, aw, ah);
+ SInt32 zoneSize = getJumpZoneSize(m_active);
+
+ // clamp position to screen
+ SInt32 xc = x, yc = y;
+ if (xc < ax + zoneSize) {
+ xc = ax;
+ }
+ else if (xc >= ax + aw - zoneSize) {
+ xc = ax + aw - 1;
+ }
+ if (yc < ay + zoneSize) {
+ yc = ay;
+ }
+ else if (yc >= ay + ah - zoneSize) {
+ yc = ay + ah - 1;
+ }
+
+ // see if we should change screens
+ // when the cursor is in a corner, there may be a screen either
+ // horizontally or vertically. check both directions.
+ EDirection dirh = kNoDirection, dirv = kNoDirection;
+ SInt32 xh = x, yv = y;
+ if (x < ax + zoneSize) {
+ xh -= zoneSize;
+ dirh = kLeft;
+ }
+ else if (x >= ax + aw - zoneSize) {
+ xh += zoneSize;
+ dirh = kRight;
+ }
+ if (y < ay + zoneSize) {
+ yv -= zoneSize;
+ dirv = kTop;
+ }
+ else if (y >= ay + ah - zoneSize) {
+ yv += zoneSize;
+ dirv = kBottom;
+ }
+ if (dirh == kNoDirection && dirv == kNoDirection) {
+ // still on local screen
+ noSwitch(x, y);
+ return false;
+ }
+
+ // check both horizontally and vertically
+ EDirection dirs[] = {dirh, dirv};
+ SInt32 xs[] = {xh, x}, ys[] = {y, yv};
+ for (int i = 0; i < 2; ++i) {
+ EDirection dir = dirs[i];
+ if (dir == kNoDirection) {
+ continue;
+ }
+ x = xs[i], y = ys[i];
+
+ // get jump destination
+ BaseClientProxy* newScreen = mapToNeighbor(m_active, dir, x, y);
+
+ // should we switch or not?
+ if (isSwitchOkay(newScreen, dir, x, y, xc, yc)) {
+ if (m_args.m_enableDragDrop
+ && m_screen->isDraggingStarted()
+ && m_active != newScreen
+ && m_waitDragInfoThread) {
+ if (m_sendDragInfoThread == NULL) {
+ m_sendDragInfoThread = new Thread(
+ new TMethodJob<Server>(
+ this,
+ &Server::sendDragInfoThread, newScreen));
+ }
+
+ return false;
+ }
+
+ // switch screen
+ switchScreen(newScreen, x, y, false);
+ m_waitDragInfoThread = true;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+Server::sendDragInfoThread(void* arg)
+{
+ BaseClientProxy* newScreen = static_cast<BaseClientProxy*>(arg);
+
+ m_dragFileList.clear();
+ String& dragFileList = m_screen->getDraggingFilename();
+ if (!dragFileList.empty()) {
+ DragInformation di;
+ di.setFilename(dragFileList);
+ m_dragFileList.push_back(di);
+ }
+
+#if defined(__APPLE__)
+ // on mac it seems that after faking a LMB up, system would signal back
+ // to barrier a mouse up event, which doesn't happen on windows. as a
+ // result, barrier would send dragging file to client twice. This variable
+ // is used to ignore the first file sending.
+ m_ignoreFileTransfer = true;
+#endif
+
+ // send drag file info to client if there is any
+ if (m_dragFileList.size() > 0) {
+ sendDragInfo(newScreen);
+ m_dragFileList.clear();
+ }
+ m_waitDragInfoThread = false;
+ m_sendDragInfoThread = NULL;
+}
+
+void
+Server::sendDragInfo(BaseClientProxy* newScreen)
+{
+ String infoString;
+ UInt32 fileCount = DragInformation::setupDragInfo(m_dragFileList, infoString);
+
+ if (fileCount > 0) {
+ char* info = NULL;
+ size_t size = infoString.size();
+ info = new char[size];
+ memcpy(info, infoString.c_str(), size);
+
+ LOG((CLOG_DEBUG2 "sending drag information to client"));
+ LOG((CLOG_DEBUG3 "dragging file list: %s", info));
+ LOG((CLOG_DEBUG3 "dragging file list string size: %i", size));
+ newScreen->sendDragInfo(fileCount, info, size);
+ }
+}
+
+void
+Server::onMouseMoveSecondary(SInt32 dx, SInt32 dy)
+{
+ LOG((CLOG_DEBUG2 "onMouseMoveSecondary %+d,%+d", dx, dy));
+
+ // mouse move on secondary (client's) screen
+ assert(m_active != NULL);
+ if (m_active == m_primaryClient) {
+ // stale event -- we're actually on the primary screen
+ return;
+ }
+
+ // if doing relative motion on secondary screens and we're locked
+ // to the screen (which activates relative moves) then send a
+ // relative mouse motion. when we're doing this we pretend as if
+ // the mouse isn't actually moving because we're expecting some
+ // program on the secondary screen to warp the mouse on us, so we
+ // have no idea where it really is.
+ if (m_relativeMoves && isLockedToScreenServer()) {
+ LOG((CLOG_DEBUG2 "relative move on %s by %d,%d", getName(m_active).c_str(), dx, dy));
+ m_active->mouseRelativeMove(dx, dy);
+ return;
+ }
+
+ // save old position
+ const SInt32 xOld = m_x;
+ const SInt32 yOld = m_y;
+
+ // save last delta
+ m_xDelta2 = m_xDelta;
+ m_yDelta2 = m_yDelta;
+
+ // save current delta
+ m_xDelta = dx;
+ m_yDelta = dy;
+
+ // accumulate motion
+ m_x += dx;
+ m_y += dy;
+
+ // get screen shape
+ SInt32 ax, ay, aw, ah;
+ m_active->getShape(ax, ay, aw, ah);
+
+ // find direction of neighbor and get the neighbor
+ bool jump = true;
+ BaseClientProxy* newScreen;
+ do {
+ // clamp position to screen
+ SInt32 xc = m_x, yc = m_y;
+ if (xc < ax) {
+ xc = ax;
+ }
+ else if (xc >= ax + aw) {
+ xc = ax + aw - 1;
+ }
+ if (yc < ay) {
+ yc = ay;
+ }
+ else if (yc >= ay + ah) {
+ yc = ay + ah - 1;
+ }
+
+ EDirection dir;
+ if (m_x < ax) {
+ dir = kLeft;
+ }
+ else if (m_x > ax + aw - 1) {
+ dir = kRight;
+ }
+ else if (m_y < ay) {
+ dir = kTop;
+ }
+ else if (m_y > ay + ah - 1) {
+ dir = kBottom;
+ }
+ else {
+ // we haven't left the screen
+ newScreen = m_active;
+ jump = false;
+
+ // if waiting and mouse is not on the border we're waiting
+ // on then stop waiting. also if it's not on the border
+ // then arm the double tap.
+ if (m_switchScreen != NULL) {
+ bool clearWait;
+ SInt32 zoneSize = m_primaryClient->getJumpZoneSize();
+ switch (m_switchDir) {
+ case kLeft:
+ clearWait = (m_x >= ax + zoneSize);
+ break;
+
+ case kRight:
+ clearWait = (m_x <= ax + aw - 1 - zoneSize);
+ break;
+
+ case kTop:
+ clearWait = (m_y >= ay + zoneSize);
+ break;
+
+ case kBottom:
+ clearWait = (m_y <= ay + ah - 1 + zoneSize);
+ break;
+
+ default:
+ clearWait = false;
+ break;
+ }
+ if (clearWait) {
+ // still on local screen
+ noSwitch(m_x, m_y);
+ }
+ }
+
+ // skip rest of block
+ break;
+ }
+
+ // try to switch screen. get the neighbor.
+ newScreen = mapToNeighbor(m_active, dir, m_x, m_y);
+
+ // see if we should switch
+ if (!isSwitchOkay(newScreen, dir, m_x, m_y, xc, yc)) {
+ newScreen = m_active;
+ jump = false;
+ }
+ } while (false);
+
+ if (jump) {
+ if (m_sendFileThread != NULL) {
+ StreamChunker::interruptFile();
+ m_sendFileThread = NULL;
+ }
+
+ SInt32 newX = m_x;
+ SInt32 newY = m_y;
+
+ // switch screens
+ switchScreen(newScreen, newX, newY, false);
+ }
+ else {
+ // same screen. clamp mouse to edge.
+ m_x = xOld + dx;
+ m_y = yOld + dy;
+ if (m_x < ax) {
+ m_x = ax;
+ LOG((CLOG_DEBUG2 "clamp to left of \"%s\"", getName(m_active).c_str()));
+ }
+ else if (m_x > ax + aw - 1) {
+ m_x = ax + aw - 1;
+ LOG((CLOG_DEBUG2 "clamp to right of \"%s\"", getName(m_active).c_str()));
+ }
+ if (m_y < ay) {
+ m_y = ay;
+ LOG((CLOG_DEBUG2 "clamp to top of \"%s\"", getName(m_active).c_str()));
+ }
+ else if (m_y > ay + ah - 1) {
+ m_y = ay + ah - 1;
+ LOG((CLOG_DEBUG2 "clamp to bottom of \"%s\"", getName(m_active).c_str()));
+ }
+
+ // warp cursor if it moved.
+ if (m_x != xOld || m_y != yOld) {
+ LOG((CLOG_DEBUG2 "move on %s to %d,%d", getName(m_active).c_str(), m_x, m_y));
+ m_active->mouseMove(m_x, m_y);
+ }
+ }
+}
+
+void
+Server::onMouseWheel(SInt32 xDelta, SInt32 yDelta)
+{
+ LOG((CLOG_DEBUG1 "onMouseWheel %+d,%+d", xDelta, yDelta));
+ assert(m_active != NULL);
+
+ // relay
+ m_active->mouseWheel(xDelta, yDelta);
+}
+
+void
+Server::onFileChunkSending(const void* data)
+{
+ FileChunk* chunk = static_cast<FileChunk*>(const_cast<void*>(data));
+
+ LOG((CLOG_DEBUG1 "sending file chunk"));
+ assert(m_active != NULL);
+
+ // relay
+ m_active->fileChunkSending(chunk->m_chunk[0], &chunk->m_chunk[1], chunk->m_dataSize);
+}
+
+void
+Server::onFileRecieveCompleted()
+{
+ if (isReceivedFileSizeValid()) {
+ m_writeToDropDirThread = new Thread(
+ new TMethodJob<Server>(
+ this, &Server::writeToDropDirThread));
+ }
+}
+
+void
+Server::writeToDropDirThread(void*)
+{
+ LOG((CLOG_DEBUG "starting write to drop dir thread"));
+
+ while (m_screen->isFakeDraggingStarted()) {
+ ARCH->sleep(.1f);
+ }
+
+ DropHelper::writeToDir(m_screen->getDropTarget(), m_fakeDragFileList,
+ m_receivedFileData);
+}
+
+bool
+Server::addClient(BaseClientProxy* client)
+{
+ String name = getName(client);
+ if (m_clients.count(name) != 0) {
+ return false;
+ }
+
+ // add event handlers
+ m_events->adoptHandler(m_events->forIScreen().shapeChanged(),
+ client->getEventTarget(),
+ new TMethodEventJob<Server>(this,
+ &Server::handleShapeChanged, client));
+ m_events->adoptHandler(m_events->forClipboard().clipboardGrabbed(),
+ client->getEventTarget(),
+ new TMethodEventJob<Server>(this,
+ &Server::handleClipboardGrabbed, client));
+ m_events->adoptHandler(m_events->forClipboard().clipboardChanged(),
+ client->getEventTarget(),
+ new TMethodEventJob<Server>(this,
+ &Server::handleClipboardChanged, client));
+
+ // add to list
+ m_clientSet.insert(client);
+ m_clients.insert(std::make_pair(name, client));
+
+ // initialize client data
+ SInt32 x, y;
+ client->getCursorPos(x, y);
+ client->setJumpCursorPos(x, y);
+
+ // tell primary client about the active sides
+ m_primaryClient->reconfigure(getActivePrimarySides());
+
+ return true;
+}
+
+bool
+Server::removeClient(BaseClientProxy* client)
+{
+ // return false if not in list
+ ClientSet::iterator i = m_clientSet.find(client);
+ if (i == m_clientSet.end()) {
+ return false;
+ }
+
+ // remove event handlers
+ m_events->removeHandler(m_events->forIScreen().shapeChanged(),
+ client->getEventTarget());
+ m_events->removeHandler(m_events->forClipboard().clipboardGrabbed(),
+ client->getEventTarget());
+ m_events->removeHandler(m_events->forClipboard().clipboardChanged(),
+ client->getEventTarget());
+
+ // remove from list
+ m_clients.erase(getName(client));
+ m_clientSet.erase(i);
+
+ return true;
+}
+
+void
+Server::closeClient(BaseClientProxy* client, const char* msg)
+{
+ assert(client != m_primaryClient);
+ assert(msg != NULL);
+
+ // send message to client. this message should cause the client
+ // to disconnect. we add this client to the closed client list
+ // and install a timer to remove the client if it doesn't respond
+ // quickly enough. we also remove the client from the active
+ // client list since we're not going to listen to it anymore.
+ // note that this method also works on clients that are not in
+ // the m_clients list. adoptClient() may call us with such a
+ // client.
+ LOG((CLOG_NOTE "disconnecting client \"%s\"", getName(client).c_str()));
+
+ // send message
+ // FIXME -- avoid type cast (kinda hard, though)
+ ((ClientProxy*)client)->close(msg);
+
+ // install timer. wait timeout seconds for client to close.
+ double timeout = 5.0;
+ EventQueueTimer* timer = m_events->newOneShotTimer(timeout, NULL);
+ m_events->adoptHandler(Event::kTimer, timer,
+ new TMethodEventJob<Server>(this,
+ &Server::handleClientCloseTimeout, client));
+
+ // move client to closing list
+ removeClient(client);
+ m_oldClients.insert(std::make_pair(client, timer));
+
+ // if this client is the active screen then we have to
+ // jump off of it
+ forceLeaveClient(client);
+}
+
+void
+Server::closeClients(const Config& config)
+{
+ // collect the clients that are connected but are being dropped
+ // from the configuration (or who's canonical name is changing).
+ typedef std::set<BaseClientProxy*> RemovedClients;
+ RemovedClients removed;
+ for (ClientList::iterator index = m_clients.begin();
+ index != m_clients.end(); ++index) {
+ if (!config.isCanonicalName(index->first)) {
+ removed.insert(index->second);
+ }
+ }
+
+ // don't close the primary client
+ removed.erase(m_primaryClient);
+
+ // now close them. we collect the list then close in two steps
+ // because closeClient() modifies the collection we iterate over.
+ for (RemovedClients::iterator index = removed.begin();
+ index != removed.end(); ++index) {
+ closeClient(*index, kMsgCClose);
+ }
+}
+
+void
+Server::removeActiveClient(BaseClientProxy* client)
+{
+ if (removeClient(client)) {
+ forceLeaveClient(client);
+ m_events->removeHandler(m_events->forClientProxy().disconnected(), client);
+ if (m_clients.size() == 1 && m_oldClients.empty()) {
+ m_events->addEvent(Event(m_events->forServer().disconnected(), this));
+ }
+ }
+}
+
+void
+Server::removeOldClient(BaseClientProxy* client)
+{
+ OldClients::iterator i = m_oldClients.find(client);
+ if (i != m_oldClients.end()) {
+ m_events->removeHandler(m_events->forClientProxy().disconnected(), client);
+ m_events->removeHandler(Event::kTimer, i->second);
+ m_events->deleteTimer(i->second);
+ m_oldClients.erase(i);
+ if (m_clients.size() == 1 && m_oldClients.empty()) {
+ m_events->addEvent(Event(m_events->forServer().disconnected(), this));
+ }
+ }
+}
+
+void
+Server::forceLeaveClient(BaseClientProxy* client)
+{
+ BaseClientProxy* active =
+ (m_activeSaver != NULL) ? m_activeSaver : m_active;
+ if (active == client) {
+ // record new position (center of primary screen)
+ m_primaryClient->getCursorCenter(m_x, m_y);
+
+ // stop waiting to switch to this client
+ if (active == m_switchScreen) {
+ stopSwitch();
+ }
+
+ // don't notify active screen since it has probably already
+ // disconnected.
+ LOG((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", getName(active).c_str(), getName(m_primaryClient).c_str(), m_x, m_y));
+
+ // cut over
+ m_active = m_primaryClient;
+
+ // enter new screen (unless we already have because of the
+ // screen saver)
+ if (m_activeSaver == NULL) {
+ m_primaryClient->enter(m_x, m_y, m_seqNum,
+ m_primaryClient->getToggleMask(), false);
+ }
+ }
+
+ // if this screen had the cursor when the screen saver activated
+ // then we can't switch back to it when the screen saver
+ // deactivates.
+ if (m_activeSaver == client) {
+ m_activeSaver = NULL;
+ }
+
+ // tell primary client about the active sides
+ m_primaryClient->reconfigure(getActivePrimarySides());
+}
+
+
+//
+// Server::ClipboardInfo
+//
+
+Server::ClipboardInfo::ClipboardInfo() :
+ m_clipboard(),
+ m_clipboardData(),
+ m_clipboardOwner(),
+ m_clipboardSeqNum(0)
+{
+ // do nothing
+}
+
+
+//
+// Server::LockCursorToScreenInfo
+//
+
+Server::LockCursorToScreenInfo*
+Server::LockCursorToScreenInfo::alloc(State state)
+{
+ LockCursorToScreenInfo* info =
+ (LockCursorToScreenInfo*)malloc(sizeof(LockCursorToScreenInfo));
+ info->m_state = state;
+ return info;
+}
+
+
+//
+// Server::SwitchToScreenInfo
+//
+
+Server::SwitchToScreenInfo*
+Server::SwitchToScreenInfo::alloc(const String& screen)
+{
+ SwitchToScreenInfo* info =
+ (SwitchToScreenInfo*)malloc(sizeof(SwitchToScreenInfo) +
+ screen.size());
+ strcpy(info->m_screen, screen.c_str());
+ return info;
+}
+
+
+//
+// Server::SwitchInDirectionInfo
+//
+
+Server::SwitchInDirectionInfo*
+Server::SwitchInDirectionInfo::alloc(EDirection direction)
+{
+ SwitchInDirectionInfo* info =
+ (SwitchInDirectionInfo*)malloc(sizeof(SwitchInDirectionInfo));
+ info->m_direction = direction;
+ return info;
+}
+
+//
+// Server::KeyboardBroadcastInfo
+//
+
+Server::KeyboardBroadcastInfo*
+Server::KeyboardBroadcastInfo::alloc(State state)
+{
+ KeyboardBroadcastInfo* info =
+ (KeyboardBroadcastInfo*)malloc(sizeof(KeyboardBroadcastInfo));
+ info->m_state = state;
+ info->m_screens[0] = '\0';
+ return info;
+}
+
+Server::KeyboardBroadcastInfo*
+Server::KeyboardBroadcastInfo::alloc(State state, const String& screens)
+{
+ KeyboardBroadcastInfo* info =
+ (KeyboardBroadcastInfo*)malloc(sizeof(KeyboardBroadcastInfo) +
+ screens.size());
+ info->m_state = state;
+ strcpy(info->m_screens, screens.c_str());
+ return info;
+}
+
+bool
+Server::isReceivedFileSizeValid()
+{
+ return m_expectedFileSize == m_receivedFileData.size();
+}
+
+void
+Server::sendFileToClient(const char* filename)
+{
+ if (m_sendFileThread != NULL) {
+ StreamChunker::interruptFile();
+ }
+
+ m_sendFileThread = new Thread(
+ new TMethodJob<Server>(
+ this, &Server::sendFileThread,
+ static_cast<void*>(const_cast<char*>(filename))));
+}
+
+void
+Server::sendFileThread(void* data)
+{
+ try {
+ char* filename = static_cast<char*>(data);
+ LOG((CLOG_DEBUG "sending file to client, filename=%s", filename));
+ StreamChunker::sendFile(filename, m_events, this);
+ }
+ catch (std::runtime_error &error) {
+ LOG((CLOG_ERR "failed sending file chunks, error: %s", error.what()));
+ }
+
+ m_sendFileThread = NULL;
+}
+
+void
+Server::dragInfoReceived(UInt32 fileNum, String content)
+{
+ if (!m_args.m_enableDragDrop) {
+ LOG((CLOG_DEBUG "drag drop not enabled, ignoring drag info."));
+ return;
+ }
+
+ DragInformation::parseDragInfo(m_fakeDragFileList, fileNum, content);
+
+ m_screen->startDraggingFiles(m_fakeDragFileList);
+}
diff --git a/src/lib/server/Server.h b/src/lib/server/Server.h
new file mode 100644
index 0000000..609af21
--- /dev/null
+++ b/src/lib/server/Server.h
@@ -0,0 +1,483 @@
+/*
+ * barrier -- 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 "server/Config.h"
+#include "barrier/clipboard_types.h"
+#include "barrier/Clipboard.h"
+#include "barrier/key_types.h"
+#include "barrier/mouse_types.h"
+#include "barrier/INode.h"
+#include "barrier/DragInformation.h"
+#include "barrier/ServerArgs.h"
+#include "base/Event.h"
+#include "base/Stopwatch.h"
+#include "base/EventTypes.h"
+#include "common/stdmap.h"
+#include "common/stdset.h"
+#include "common/stdvector.h"
+
+class BaseClientProxy;
+class EventQueueTimer;
+class PrimaryClient;
+class InputFilter;
+namespace barrier { class Screen; }
+class IEventQueue;
+class Thread;
+class ClientListener;
+
+//! Barrier server
+/*!
+This class implements the top-level server algorithms for barrier.
+*/
+class Server : public INode {
+public:
+ //! Lock cursor to screen data
+ class LockCursorToScreenInfo {
+ public:
+ enum State { kOff, kOn, kToggle };
+
+ static LockCursorToScreenInfo* alloc(State state = kToggle);
+
+ public:
+ State m_state;
+ };
+
+ //! Switch to screen data
+ class SwitchToScreenInfo {
+ public:
+ static SwitchToScreenInfo* alloc(const String& screen);
+
+ public:
+ // this is a C-string; this type is a variable size structure
+ char m_screen[1];
+ };
+
+ //! Switch in direction data
+ class SwitchInDirectionInfo {
+ public:
+ static SwitchInDirectionInfo* alloc(EDirection direction);
+
+ public:
+ EDirection m_direction;
+ };
+
+ //! Screen connected data
+ class ScreenConnectedInfo {
+ public:
+ ScreenConnectedInfo(String screen) : m_screen(screen) { }
+
+ public:
+ String m_screen; // was char[1]
+ };
+
+ //! Keyboard broadcast data
+ class KeyboardBroadcastInfo {
+ public:
+ enum State { kOff, kOn, kToggle };
+
+ static KeyboardBroadcastInfo* alloc(State state = kToggle);
+ static KeyboardBroadcastInfo* alloc(State state,
+ const String& screens);
+
+ public:
+ State m_state;
+ char m_screens[1];
+ };
+
+ /*!
+ Start the server with the configuration \p config and the primary
+ client (local screen) \p primaryClient. The client retains
+ ownership of \p primaryClient.
+ */
+ Server(Config& config, PrimaryClient* primaryClient,
+ barrier::Screen* screen, IEventQueue* events, ServerArgs const& args);
+ ~Server();
+
+#ifdef TEST_ENV
+ Server() : m_mock(true), m_config(NULL) { }
+ void setActive(BaseClientProxy* active) { m_active = active; }
+#endif
+
+ //! @name manipulators
+ //@{
+
+ //! Set configuration
+ /*!
+ Change the server's configuration. Returns true iff the new
+ configuration was accepted (it must include the server's name).
+ This will disconnect any clients no longer in the configuration.
+ */
+ bool setConfig(const Config&);
+
+ //! Add a client
+ /*!
+ Adds \p client to the server. The client is adopted and will be
+ destroyed when the client disconnects or is disconnected.
+ */
+ void adoptClient(BaseClientProxy* client);
+
+ //! Disconnect clients
+ /*!
+ Disconnect clients. This tells them to disconnect but does not wait
+ for them to actually do so. The server sends the disconnected event
+ when they're all disconnected (or immediately if none are connected).
+ The caller can also just destroy this object to force the disconnection.
+ */
+ void disconnect();
+
+ //! Create a new thread and use it to send file to client
+ void sendFileToClient(const char* filename);
+
+ //! Received dragging information from client
+ void dragInfoReceived(UInt32 fileNum, String content);
+
+ //! Store ClientListener pointer
+ void setListener(ClientListener* p) { m_clientListener = p; }
+
+ //@}
+ //! @name accessors
+ //@{
+
+ //! Get number of connected clients
+ /*!
+ Returns the number of connected clients, including the server itself.
+ */
+ UInt32 getNumClients() const;
+
+ //! Get the list of connected clients
+ /*!
+ Set the \c list to the names of the currently connected clients.
+ */
+ void getClients(std::vector<String>& list) const;
+
+ //! Return true if recieved file size is valid
+ bool isReceivedFileSizeValid();
+
+ //! Return expected file data size
+ size_t& getExpectedFileSize() { return m_expectedFileSize; }
+
+ //! Return received file data
+ String& getReceivedFileData() { return m_receivedFileData; }
+
+ //! Return fake drag file list
+ DragFileList getFakeDragFileList() { return m_fakeDragFileList; }
+
+ //@}
+
+private:
+ // get canonical name of client
+ String getName(const BaseClientProxy*) const;
+
+ // get the sides of the primary screen that have neighbors
+ UInt32 getActivePrimarySides() const;
+
+ // returns true iff mouse should be locked to the current screen
+ // according to this object only, ignoring what the primary client
+ // says.
+ bool isLockedToScreenServer() const;
+
+ // returns true iff mouse should be locked to the current screen
+ // according to this object or the primary client.
+ bool isLockedToScreen() const;
+
+ // returns the jump zone of the client
+ SInt32 getJumpZoneSize(BaseClientProxy*) const;
+
+ // change the active screen
+ void switchScreen(BaseClientProxy*,
+ SInt32 x, SInt32 y, bool forScreenSaver);
+
+ // jump to screen
+ void jumpToScreen(BaseClientProxy*);
+
+ // convert pixel position to fraction, using x or y depending on the
+ // direction.
+ float mapToFraction(BaseClientProxy*, EDirection,
+ SInt32 x, SInt32 y) const;
+
+ // convert fraction to pixel position, writing only x or y depending
+ // on the direction.
+ void mapToPixel(BaseClientProxy*, EDirection, float f,
+ SInt32& x, SInt32& y) const;
+
+ // returns true if the client has a neighbor anywhere along the edge
+ // indicated by the direction.
+ bool hasAnyNeighbor(BaseClientProxy*, EDirection) const;
+
+ // lookup neighboring screen, mapping the coordinate independent of
+ // the direction to the neighbor's coordinate space.
+ BaseClientProxy* getNeighbor(BaseClientProxy*, EDirection,
+ SInt32& x, SInt32& y) const;
+
+ // lookup neighboring screen. given a position relative to the
+ // source screen, find the screen we should move onto and where.
+ // if the position is sufficiently far from the source then we
+ // cross multiple screens. if there is no suitable screen then
+ // return NULL and x,y are not modified.
+ BaseClientProxy* mapToNeighbor(BaseClientProxy*, EDirection,
+ SInt32& x, SInt32& y) const;
+
+ // adjusts x and y or neither to avoid ending up in a jump zone
+ // after entering the client in the given direction.
+ void avoidJumpZone(BaseClientProxy*, EDirection,
+ SInt32& x, SInt32& y) const;
+
+ // test if a switch is permitted. this includes testing user
+ // options like switch delay and tracking any state required to
+ // implement them. returns true iff a switch is permitted.
+ bool isSwitchOkay(BaseClientProxy* dst, EDirection,
+ SInt32 x, SInt32 y, SInt32 xActive, SInt32 yActive);
+
+ // update switch state due to a mouse move at \p x, \p y that
+ // doesn't switch screens.
+ void noSwitch(SInt32 x, SInt32 y);
+
+ // stop switch timers
+ void stopSwitch();
+
+ // start two tap switch timer
+ void startSwitchTwoTap();
+
+ // arm the two tap switch timer if \p x, \p y is outside the tap zone
+ void armSwitchTwoTap(SInt32 x, SInt32 y);
+
+ // stop the two tap switch timer
+ void stopSwitchTwoTap();
+
+ // returns true iff the two tap switch timer is started
+ bool isSwitchTwoTapStarted() const;
+
+ // returns true iff should switch because of two tap
+ bool shouldSwitchTwoTap() const;
+
+ // start delay switch timer
+ void startSwitchWait(SInt32 x, SInt32 y);
+
+ // stop delay switch timer
+ void stopSwitchWait();
+
+ // returns true iff the delay switch timer is started
+ bool isSwitchWaitStarted() const;
+
+ // returns the corner (EScreenSwitchCornerMasks) where x,y is on the
+ // given client. corners have the given size.
+ UInt32 getCorner(BaseClientProxy*,
+ SInt32 x, SInt32 y, SInt32 size) const;
+
+ // stop relative mouse moves
+ void stopRelativeMoves();
+
+ // send screen options to \c client
+ void sendOptions(BaseClientProxy* client) const;
+
+ // process options from configuration
+ void processOptions();
+
+ // event handlers
+ void handleShapeChanged(const Event&, void*);
+ void handleClipboardGrabbed(const Event&, void*);
+ void handleClipboardChanged(const Event&, void*);
+ void handleKeyDownEvent(const Event&, void*);
+ void handleKeyUpEvent(const Event&, void*);
+ void handleKeyRepeatEvent(const Event&, void*);
+ void handleButtonDownEvent(const Event&, void*);
+ void handleButtonUpEvent(const Event&, void*);
+ void handleMotionPrimaryEvent(const Event&, void*);
+ void handleMotionSecondaryEvent(const Event&, void*);
+ void handleWheelEvent(const Event&, void*);
+ void handleScreensaverActivatedEvent(const Event&, void*);
+ void handleScreensaverDeactivatedEvent(const Event&, void*);
+ void handleSwitchWaitTimeout(const Event&, void*);
+ void handleClientDisconnected(const Event&, void*);
+ void handleClientCloseTimeout(const Event&, void*);
+ void handleSwitchToScreenEvent(const Event&, void*);
+ void handleSwitchInDirectionEvent(const Event&, void*);
+ void handleKeyboardBroadcastEvent(const Event&,void*);
+ void handleLockCursorToScreenEvent(const Event&, void*);
+ void handleFakeInputBeginEvent(const Event&, void*);
+ void handleFakeInputEndEvent(const Event&, void*);
+ void handleFileChunkSendingEvent(const Event&, void*);
+ void handleFileRecieveCompletedEvent(const Event&, void*);
+
+ // event processing
+ void onClipboardChanged(BaseClientProxy* sender,
+ ClipboardID id, UInt32 seqNum);
+ void onScreensaver(bool activated);
+ void onKeyDown(KeyID, KeyModifierMask, KeyButton,
+ const char* screens);
+ void onKeyUp(KeyID, KeyModifierMask, KeyButton,
+ const char* screens);
+ void onKeyRepeat(KeyID, KeyModifierMask, SInt32, KeyButton);
+ void onMouseDown(ButtonID);
+ void onMouseUp(ButtonID);
+ bool onMouseMovePrimary(SInt32 x, SInt32 y);
+ void onMouseMoveSecondary(SInt32 dx, SInt32 dy);
+ void onMouseWheel(SInt32 xDelta, SInt32 yDelta);
+ void onFileChunkSending(const void* data);
+ void onFileRecieveCompleted();
+
+ // add client to list and attach event handlers for client
+ bool addClient(BaseClientProxy*);
+
+ // remove client from list and detach event handlers for client
+ bool removeClient(BaseClientProxy*);
+
+ // close a client
+ void closeClient(BaseClientProxy*, const char* msg);
+
+ // close clients not in \p config
+ void closeClients(const Config& config);
+
+ // close all clients whether they've completed the handshake or not,
+ // except the primary client
+ void closeAllClients();
+
+ // remove clients from internal state
+ void removeActiveClient(BaseClientProxy*);
+ void removeOldClient(BaseClientProxy*);
+
+ // force the cursor off of \p client
+ void forceLeaveClient(BaseClientProxy* client);
+
+ // thread funciton for sending file
+ void sendFileThread(void*);
+
+ // thread function for writing file to drop directory
+ void writeToDropDirThread(void*);
+
+ // thread function for sending drag information
+ void sendDragInfoThread(void*);
+
+ // send drag info to new client screen
+ void sendDragInfo(BaseClientProxy* newScreen);
+
+public:
+ bool m_mock;
+
+private:
+ class ClipboardInfo {
+ public:
+ ClipboardInfo();
+
+ public:
+ Clipboard m_clipboard;
+ String m_clipboardData;
+ String m_clipboardOwner;
+ UInt32 m_clipboardSeqNum;
+ };
+
+ // the primary screen client
+ PrimaryClient* m_primaryClient;
+
+ // all clients (including the primary client) indexed by name
+ typedef std::map<String, BaseClientProxy*> ClientList;
+ typedef std::set<BaseClientProxy*> ClientSet;
+ ClientList m_clients;
+ ClientSet m_clientSet;
+
+ // all old connections that we're waiting to hangup
+ typedef std::map<BaseClientProxy*, EventQueueTimer*> OldClients;
+ OldClients m_oldClients;
+
+ // the client with focus
+ BaseClientProxy* m_active;
+
+ // the sequence number of enter messages
+ UInt32 m_seqNum;
+
+ // current mouse position (in absolute screen coordinates) on
+ // whichever screen is active
+ SInt32 m_x, m_y;
+
+ // last mouse deltas. this is needed to smooth out double tap
+ // on win32 which reports bogus mouse motion at the edge of
+ // the screen when using low level hooks, synthesizing motion
+ // in the opposite direction the mouse actually moved.
+ SInt32 m_xDelta, m_yDelta;
+ SInt32 m_xDelta2, m_yDelta2;
+
+ // current configuration
+ Config* m_config;
+
+ // input filter (from m_config);
+ InputFilter* m_inputFilter;
+
+ // clipboard cache
+ ClipboardInfo m_clipboards[kClipboardEnd];
+
+ // state saved when screen saver activates
+ BaseClientProxy* m_activeSaver;
+ SInt32 m_xSaver, m_ySaver;
+
+ // common state for screen switch tests. all tests are always
+ // trying to reach the same screen in the same direction.
+ EDirection m_switchDir;
+ BaseClientProxy* m_switchScreen;
+
+ // state for delayed screen switching
+ double m_switchWaitDelay;
+ EventQueueTimer* m_switchWaitTimer;
+ SInt32 m_switchWaitX, m_switchWaitY;
+
+ // state for double-tap screen switching
+ double m_switchTwoTapDelay;
+ Stopwatch m_switchTwoTapTimer;
+ bool m_switchTwoTapEngaged;
+ bool m_switchTwoTapArmed;
+ SInt32 m_switchTwoTapZone;
+
+ // modifiers needed before switching
+ bool m_switchNeedsShift;
+ bool m_switchNeedsControl;
+ bool m_switchNeedsAlt;
+
+ // relative mouse move option
+ bool m_relativeMoves;
+
+ // flag whether or not we have broadcasting enabled and the screens to
+ // which we should send broadcasted keys.
+ bool m_keyboardBroadcasting;
+ String m_keyboardBroadcastingScreens;
+
+ // screen locking (former scroll lock)
+ bool m_lockedToScreen;
+
+ // server screen
+ barrier::Screen* m_screen;
+
+ IEventQueue* m_events;
+
+ // file transfer
+ size_t m_expectedFileSize;
+ String m_receivedFileData;
+ DragFileList m_dragFileList;
+ DragFileList m_fakeDragFileList;
+ Thread* m_sendFileThread;
+ Thread* m_writeToDropDirThread;
+ String m_dragFileExt;
+ bool m_ignoreFileTransfer;
+ bool m_enableClipboard;
+
+ Thread* m_sendDragInfoThread;
+ bool m_waitDragInfoThread;
+
+ ClientListener* m_clientListener;
+ ServerArgs m_args;
+};
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
new file mode 100644
index 0000000..daecb31
--- /dev/null
+++ b/src/test/CMakeLists.txt
@@ -0,0 +1,33 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2011 Nick Bolton
+#
+# 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_directories(
+ ../../ext/gtest
+ ../../ext/gtest/include
+ ../../ext/gmock
+ ../../ext/gmock/include)
+
+add_library(gtest STATIC ../../ext/gtest/src/gtest-all.cc)
+add_library(gmock STATIC ../../ext/gmock/src/gmock-all.cc)
+
+if (UNIX)
+ # ignore warnings in gtest and gmock
+ set_target_properties(gtest PROPERTIES COMPILE_FLAGS "-w")
+ set_target_properties(gmock PROPERTIES COMPILE_FLAGS "-w")
+endif()
+
+add_subdirectory(integtests)
+add_subdirectory(unittests)
diff --git a/src/test/global/TestEventQueue.cpp b/src/test/global/TestEventQueue.cpp
new file mode 100644
index 0000000..d202922
--- /dev/null
+++ b/src/test/global/TestEventQueue.cpp
@@ -0,0 +1,53 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/TestEventQueue.h"
+
+#include "base/Log.h"
+#include "base/TMethodEventJob.h"
+#include "base/SimpleEventQueueBuffer.h"
+#include "common/stdexcept.h"
+
+void
+TestEventQueue::raiseQuitEvent()
+{
+ addEvent(Event(Event::kQuit));
+}
+
+void
+TestEventQueue::initQuitTimeout(double timeout)
+{
+ assert(m_quitTimeoutTimer == nullptr);
+ m_quitTimeoutTimer = newOneShotTimer(timeout, NULL);
+ adoptHandler(Event::kTimer, m_quitTimeoutTimer,
+ new TMethodEventJob<TestEventQueue>(
+ this, &TestEventQueue::handleQuitTimeout));
+}
+
+void
+TestEventQueue::cleanupQuitTimeout()
+{
+ removeHandler(Event::kTimer, m_quitTimeoutTimer);
+ delete m_quitTimeoutTimer;
+ m_quitTimeoutTimer = nullptr;
+}
+
+void
+TestEventQueue::handleQuitTimeout(const Event&, void* vclient)
+{
+ throw std::runtime_error("test event queue timeout");
+}
diff --git a/src/test/global/TestEventQueue.h b/src/test/global/TestEventQueue.h
new file mode 100644
index 0000000..a6cf0ca
--- /dev/null
+++ b/src/test/global/TestEventQueue.h
@@ -0,0 +1,38 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#include "base/EventQueue.h"
+
+class EventQueueTimer;
+
+class TestEventQueue : public EventQueue {
+public:
+ TestEventQueue() : m_quitTimeoutTimer(nullptr) { }
+
+ void handleQuitTimeout(const Event&, void* vclient);
+ void raiseQuitEvent();
+ void initQuitTimeout(double timeout);
+ void cleanupQuitTimeout();
+
+private:
+ void timeoutThread(void*);
+
+private:
+ EventQueueTimer* m_quitTimeoutTimer;
+};
diff --git a/src/test/global/gmock.h b/src/test/global/gmock.h
new file mode 100644
index 0000000..64597f4
--- /dev/null
+++ b/src/test/global/gmock.h
@@ -0,0 +1,32 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+// HACK: gcc on osx106 doesn't give you an easy way to hide warnings
+// from included headers, so use the system_header pragma. the downside
+// is that everything in the header file following this also has warnings
+// ignored, so we need to put it in a separate header file.
+#if __APPLE__
+# pragma GCC system_header
+#endif
+
+// gmock includes gtest which has a warning on osx106 (signed/unsigned
+// int compare), so include our special header here first to silence
+// the warning.
+#include "test/global/gtest.h"
+#include <gmock/gmock.h>
diff --git a/src/test/global/gtest.h b/src/test/global/gtest.h
new file mode 100644
index 0000000..0b2acbc
--- /dev/null
+++ b/src/test/global/gtest.h
@@ -0,0 +1,29 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+// HACK: gcc on osx106 doesn't give you an easy way to hide warnings
+// from included headers, so use the system_header pragma. the downside
+// is that everything in the header file following this also has warnings
+// ignored, so we need to put it in a separate header file.
+#if __APPLE__
+# pragma GCC system_header
+#endif
+
+// gtest has a warning on osx106 (signed/unsigned int compare).
+#include <gtest/gtest.h>
diff --git a/src/test/guitests/guitests.pro b/src/test/guitests/guitests.pro
new file mode 100644
index 0000000..3be7e0d
--- /dev/null
+++ b/src/test/guitests/guitests.pro
@@ -0,0 +1,16 @@
+QT += network
+QT -= gui
+TARGET = guitests
+CONFIG += qtestlib
+CONFIG += console
+CONFIG -= app_bundle
+TEMPLATE = app
+INCLUDEPATH += ../../gui/src
+SOURCES += src/main.cpp \
+ src/VersionCheckerTests.cpp
+HEADERS += src/VersionCheckerTests.h
+win32 {
+ Debug:DESTDIR = ../../../bin/Debug
+ Release:DESTDIR = ../../../bin/Release
+}
+else:DESTDIR = ../../../bin
diff --git a/src/test/guitests/src/VersionCheckerTests.cpp b/src/test/guitests/src/VersionCheckerTests.cpp
new file mode 100644
index 0000000..0efc5f9
--- /dev/null
+++ b/src/test/guitests/src/VersionCheckerTests.cpp
@@ -0,0 +1,47 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "VersionCheckerTests.h"
+#include "VersionChecker.cpp"
+#include "../../gui/tmp/release/moc_VersionChecker.cpp"
+
+#include <QtTest/QTest>
+
+void VersionCheckerTests::compareVersions()
+{
+ VersionChecker versionChecker;
+
+ // compare majors
+ QCOMPARE(versionChecker.compareVersions("1.0.0", "2.0.0"), 1);
+ QCOMPARE(versionChecker.compareVersions("2.0.0", "1.0.0"), -1);
+ QCOMPARE(versionChecker.compareVersions("1.0.0", "1.0.0"), 0);
+ QCOMPARE(versionChecker.compareVersions("1.4.8", "2.4.7"), 1);
+ QCOMPARE(versionChecker.compareVersions("2.4.7", "1.4.8"), -1);
+
+ // compare minors
+ QCOMPARE(versionChecker.compareVersions("1.3.0", "1.4.0"), 1);
+ QCOMPARE(versionChecker.compareVersions("1.4.0", "1.3.0"), -1);
+ QCOMPARE(versionChecker.compareVersions("1.4.0", "1.4.0"), 0);
+ QCOMPARE(versionChecker.compareVersions("1.3.8", "1.4.7"), 1);
+ QCOMPARE(versionChecker.compareVersions("1.4.7", "1.3.8"), -1);
+
+ // compare revs
+ QCOMPARE(versionChecker.compareVersions("1.4.7", "1.4.8"), 1);
+ QCOMPARE(versionChecker.compareVersions("1.4.8", "1.4.7"), -1);
+ QCOMPARE(versionChecker.compareVersions("1.4.7", "1.4.7"), 0);
+}
diff --git a/src/test/guitests/src/VersionCheckerTests.h b/src/test/guitests/src/VersionCheckerTests.h
new file mode 100644
index 0000000..7884f3a
--- /dev/null
+++ b/src/test/guitests/src/VersionCheckerTests.h
@@ -0,0 +1,28 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 "qobject.h"
+
+class VersionCheckerTests : public QObject
+{
+ Q_OBJECT
+private slots:
+ void compareVersions();
+};
diff --git a/src/test/guitests/src/main.cpp b/src/test/guitests/src/main.cpp
new file mode 100644
index 0000000..2ff6e72
--- /dev/null
+++ b/src/test/guitests/src/main.cpp
@@ -0,0 +1,26 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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 <QtTest/QTest>
+#include "VersionCheckerTests.h"
+
+int main(int argc, char *argv[])
+{
+ VersionCheckerTests versionCheckerTests;
+ QTest::qExec(&versionCheckerTests, argc, argv);
+}
diff --git a/src/test/integtests/CMakeLists.txt b/src/test/integtests/CMakeLists.txt
new file mode 100644
index 0000000..a412ecc
--- /dev/null
+++ b/src/test/integtests/CMakeLists.txt
@@ -0,0 +1,71 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB_RECURSE headers "*.h")
+file(GLOB_RECURSE sources "*.cpp")
+
+# remove platform files (specific platform added later).
+file(GLOB_RECURSE remove_platform "platform/*")
+list(REMOVE_ITEM headers ${remove_platform})
+list(REMOVE_ITEM sources ${remove_platform})
+
+# platform
+if (WIN32)
+ file(GLOB platform_sources "platform/MSWindows*.cpp")
+ file(GLOB platform_headers "platform/MSWindows*.h")
+elseif (APPLE)
+ file(GLOB platform_sources "platform/OSX*.cpp")
+ file(GLOB platform_headers "platform/OSX*.h")
+elseif (UNIX)
+ file(GLOB platform_sources "platform/XWindows*.cpp")
+ file(GLOB platform_headers "platform/XWindows*.h")
+endif()
+
+list(APPEND sources ${platform_sources})
+list(APPEND headers ${platform_headers})
+
+file(GLOB_RECURSE global_headers "../../test/global/*.h")
+file(GLOB_RECURSE global_sources "../../test/global/*.cpp")
+
+list(APPEND headers ${global_headers})
+list(APPEND sources ${global_sources})
+
+file(GLOB_RECURSE mock_headers "../../test/mock/*.h")
+file(GLOB_RECURSE mock_sources "../../test/mock/*.cpp")
+
+list(APPEND headers ${mock_headers})
+list(APPEND sources ${mock_sources})
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+include_directories(
+ ../../
+ ../../lib/
+ ../../../ext/gtest/include
+ ../../../ext/gmock/include
+)
+
+if (UNIX)
+ include_directories(
+ ../../..
+ )
+endif()
+
+add_executable(integtests ${sources})
+target_link_libraries(integtests
+ arch base client common io ipc mt net platform server barrier gtest gmock ${libs} ${OPENSSL_LIBS})
diff --git a/src/test/integtests/Main.cpp b/src/test/integtests/Main.cpp
new file mode 100644
index 0000000..76b42b6
--- /dev/null
+++ b/src/test/integtests/Main.cpp
@@ -0,0 +1,108 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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 "arch/Arch.h"
+#include "base/Log.h"
+
+#if SYSAPI_WIN32
+#include "arch/win32/ArchMiscWindows.h"
+#endif
+
+#include "test/global/gtest.h"
+#include <iostream>
+#include <fstream>
+
+#define LOCK_TIMEOUT 30
+
+using namespace std;
+
+void lock(string lockFile);
+void unlock(string lockFile);
+
+int
+main(int argc, char **argv)
+{
+#if SYSAPI_WIN32
+ // record window instance for tray icon, etc
+ ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
+#endif
+
+ Arch arch;
+ arch.init();
+
+ Log log;
+ log.setFilter(kDEBUG2);
+
+ string lockFile;
+ for (int i = 0; i < argc; i++) {
+ if (string(argv[i]).compare("--lock-file") == 0) {
+ lockFile = argv[i + 1];
+ }
+ }
+
+ if (!lockFile.empty()) {
+ lock(lockFile);
+ }
+
+
+ testing::InitGoogleTest(&argc, argv);
+
+ int result = RUN_ALL_TESTS();
+
+ if (!lockFile.empty()) {
+ unlock(lockFile);
+ }
+
+ // gtest seems to randomly finish with error codes (e.g. -1, -1073741819)
+ // even when no tests have failed. not sure what causes this, but it
+ // happens on all platforms and keeps leading to false positives.
+ // according to the documentation, 1 is a failure, so we should be
+ // able to trust that code.
+ return (result == 1) ? 1 : 0;
+}
+
+void
+lock(string lockFile)
+{
+ double start = ARCH->time();
+
+ // keep checking until timeout is reached.
+ while ((ARCH->time() - start) < LOCK_TIMEOUT) {
+
+ ifstream is(lockFile.c_str());
+ bool noLock = !is;
+ is.close();
+
+ if (noLock) {
+ break;
+ }
+
+ // check every second if file has gone.
+ ARCH->sleep(1);
+ }
+
+ // write empty lock file.
+ ofstream os(lockFile.c_str());
+ os.close();
+}
+
+void
+unlock(string lockFile)
+{
+ remove(lockFile.c_str());
+}
diff --git a/src/test/integtests/arch/ArchInternetTests.cpp b/src/test/integtests/arch/ArchInternetTests.cpp
new file mode 100644
index 0000000..722df2f
--- /dev/null
+++ b/src/test/integtests/arch/ArchInternetTests.cpp
@@ -0,0 +1,37 @@
+/*
+ * barrier -- 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 "arch/Arch.h"
+
+#include "test/global/gtest.h"
+
+#define TEST_URL "https://symless.com/tests/?testString"
+//#define TEST_URL "http://localhost/barrier/tests/?testString"
+
+TEST(ArchInternetTests, get)
+{
+ ARCH_INTERNET internet;
+ String result = internet.get(TEST_URL);
+ ASSERT_EQ("Hello world!", result);
+}
+
+TEST(ArchInternetTests, urlEncode)
+{
+ ARCH_INTERNET internet;
+ String result = internet.urlEncode("hello=+&world");
+ ASSERT_EQ("hello%3D%2B%26world", result);
+}
diff --git a/src/test/integtests/ipc/IpcTests.cpp b/src/test/integtests/ipc/IpcTests.cpp
new file mode 100644
index 0000000..a0ee241
--- /dev/null
+++ b/src/test/integtests/ipc/IpcTests.cpp
@@ -0,0 +1,211 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2012 Nick Bolton
+ *
+ * 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/>.
+ */
+
+// TODO: fix, tests failing intermittently on mac.
+#ifndef WINAPI_CARBON
+
+#define TEST_ENV
+
+#include "test/global/TestEventQueue.h"
+#include "ipc/IpcServer.h"
+#include "ipc/IpcClient.h"
+#include "ipc/IpcServerProxy.h"
+#include "ipc/IpcMessage.h"
+#include "ipc/IpcClientProxy.h"
+#include "ipc/Ipc.h"
+#include "net/SocketMultiplexer.h"
+#include "mt/Thread.h"
+#include "arch/Arch.h"
+#include "base/TMethodJob.h"
+#include "base/String.h"
+#include "base/Log.h"
+#include "base/EventQueue.h"
+#include "base/TMethodEventJob.h"
+
+#include "test/global/gtest.h"
+
+#define TEST_IPC_PORT 24802
+
+class IpcTests : public ::testing::Test
+{
+public:
+ IpcTests();
+ virtual ~IpcTests();
+
+ void connectToServer_handleMessageReceived(const Event&, void*);
+ void sendMessageToServer_serverHandleMessageReceived(const Event&, void*);
+ void sendMessageToClient_serverHandleClientConnected(const Event&, void*);
+ void sendMessageToClient_clientHandleMessageReceived(const Event&, void*);
+
+public:
+ SocketMultiplexer m_multiplexer;
+ bool m_connectToServer_helloMessageReceived;
+ bool m_connectToServer_hasClientNode;
+ IpcServer* m_connectToServer_server;
+ String m_sendMessageToServer_receivedString;
+ String m_sendMessageToClient_receivedString;
+ IpcClient* m_sendMessageToServer_client;
+ IpcServer* m_sendMessageToClient_server;
+ TestEventQueue m_events;
+
+};
+
+TEST_F(IpcTests, connectToServer)
+{
+ SocketMultiplexer socketMultiplexer;
+ IpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
+ server.listen();
+ m_connectToServer_server = &server;
+
+ m_events.adoptHandler(
+ m_events.forIpcServer().messageReceived(), &server,
+ new TMethodEventJob<IpcTests>(
+ this, &IpcTests::connectToServer_handleMessageReceived));
+
+ IpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
+ client.connect();
+
+ m_events.initQuitTimeout(5);
+ m_events.loop();
+ m_events.removeHandler(m_events.forIpcServer().messageReceived(), &server);
+ m_events.cleanupQuitTimeout();
+
+ EXPECT_EQ(true, m_connectToServer_helloMessageReceived);
+ EXPECT_EQ(true, m_connectToServer_hasClientNode);
+}
+
+TEST_F(IpcTests, sendMessageToServer)
+{
+ SocketMultiplexer socketMultiplexer;
+ IpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
+ server.listen();
+
+ // event handler sends "test" command to server.
+ m_events.adoptHandler(
+ m_events.forIpcServer().messageReceived(), &server,
+ new TMethodEventJob<IpcTests>(
+ this, &IpcTests::sendMessageToServer_serverHandleMessageReceived));
+
+ IpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
+ client.connect();
+ m_sendMessageToServer_client = &client;
+
+ m_events.initQuitTimeout(5);
+ m_events.loop();
+ m_events.removeHandler(m_events.forIpcServer().messageReceived(), &server);
+ m_events.cleanupQuitTimeout();
+
+ EXPECT_EQ("test", m_sendMessageToServer_receivedString);
+}
+
+TEST_F(IpcTests, sendMessageToClient)
+{
+ SocketMultiplexer socketMultiplexer;
+ IpcServer server(&m_events, &socketMultiplexer, TEST_IPC_PORT);
+ server.listen();
+ m_sendMessageToClient_server = &server;
+
+ // event handler sends "test" log line to client.
+ m_events.adoptHandler(
+ m_events.forIpcServer().messageReceived(), &server,
+ new TMethodEventJob<IpcTests>(
+ this, &IpcTests::sendMessageToClient_serverHandleClientConnected));
+
+ IpcClient client(&m_events, &socketMultiplexer, TEST_IPC_PORT);
+ client.connect();
+
+ m_events.adoptHandler(
+ m_events.forIpcClient().messageReceived(), &client,
+ new TMethodEventJob<IpcTests>(
+ this, &IpcTests::sendMessageToClient_clientHandleMessageReceived));
+
+ m_events.initQuitTimeout(5);
+ m_events.loop();
+ m_events.removeHandler(m_events.forIpcServer().messageReceived(), &server);
+ m_events.removeHandler(m_events.forIpcClient().messageReceived(), &client);
+ m_events.cleanupQuitTimeout();
+
+ EXPECT_EQ("test", m_sendMessageToClient_receivedString);
+}
+
+IpcTests::IpcTests() :
+m_connectToServer_helloMessageReceived(false),
+m_connectToServer_hasClientNode(false),
+m_connectToServer_server(nullptr),
+m_sendMessageToClient_server(nullptr),
+m_sendMessageToServer_client(nullptr)
+{
+}
+
+IpcTests::~IpcTests()
+{
+}
+
+void
+IpcTests::connectToServer_handleMessageReceived(const Event& e, void*)
+{
+ IpcMessage* m = static_cast<IpcMessage*>(e.getDataObject());
+ if (m->type() == kIpcHello) {
+ m_connectToServer_hasClientNode =
+ m_connectToServer_server->hasClients(kIpcClientNode);
+ m_connectToServer_helloMessageReceived = true;
+ m_events.raiseQuitEvent();
+ }
+}
+
+void
+IpcTests::sendMessageToServer_serverHandleMessageReceived(const Event& e, void*)
+{
+ IpcMessage* m = static_cast<IpcMessage*>(e.getDataObject());
+ if (m->type() == kIpcHello) {
+ LOG((CLOG_DEBUG "client said hello, sending test to server"));
+ IpcCommandMessage m("test", true);
+ m_sendMessageToServer_client->send(m);
+ }
+ else if (m->type() == kIpcCommand) {
+ IpcCommandMessage* cm = static_cast<IpcCommandMessage*>(m);
+ LOG((CLOG_DEBUG "got ipc command message, %d", cm->command().c_str()));
+ m_sendMessageToServer_receivedString = cm->command();
+ m_events.raiseQuitEvent();
+ }
+}
+
+void
+IpcTests::sendMessageToClient_serverHandleClientConnected(const Event& e, void*)
+{
+ IpcMessage* m = static_cast<IpcMessage*>(e.getDataObject());
+ if (m->type() == kIpcHello) {
+ LOG((CLOG_DEBUG "client said hello, sending test to client"));
+ IpcLogLineMessage m("test");
+ m_sendMessageToClient_server->send(m, kIpcClientNode);
+ }
+}
+
+void
+IpcTests::sendMessageToClient_clientHandleMessageReceived(const Event& e, void*)
+{
+ IpcMessage* m = static_cast<IpcMessage*>(e.getDataObject());
+ if (m->type() == kIpcLogLine) {
+ IpcLogLineMessage* llm = static_cast<IpcLogLineMessage*>(m);
+ LOG((CLOG_DEBUG "got ipc log message, %d", llm->logLine().c_str()));
+ m_sendMessageToClient_receivedString = llm->logLine();
+ m_events.raiseQuitEvent();
+ }
+}
+
+#endif // WINAPI_CARBON
diff --git a/src/test/integtests/net/NetworkTests.cpp b/src/test/integtests/net/NetworkTests.cpp
new file mode 100644
index 0000000..4a9a9f0
--- /dev/null
+++ b/src/test/integtests/net/NetworkTests.cpp
@@ -0,0 +1,525 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+// TODO: fix, tests failing intermittently on mac.
+#ifndef WINAPI_CARBON
+
+#define TEST_ENV
+
+#include "test/mock/server/MockConfig.h"
+#include "test/mock/server/MockPrimaryClient.h"
+#include "test/mock/barrier/MockScreen.h"
+#include "test/mock/server/MockInputFilter.h"
+#include "test/global/TestEventQueue.h"
+#include "server/Server.h"
+#include "server/ClientListener.h"
+#include "server/ClientProxy.h"
+#include "client/Client.h"
+#include "barrier/FileChunk.h"
+#include "barrier/StreamChunker.h"
+#include "net/SocketMultiplexer.h"
+#include "net/NetworkAddress.h"
+#include "net/TCPSocketFactory.h"
+#include "mt/Thread.h"
+#include "base/TMethodEventJob.h"
+#include "base/TMethodJob.h"
+#include "base/Log.h"
+#include "common/stdexcept.h"
+
+#include "test/global/gtest.h"
+#include <sstream>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+
+using namespace std;
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::Invoke;
+
+#define TEST_PORT 24803
+#define TEST_HOST "localhost"
+
+const size_t kMockDataSize = 1024 * 1024 * 10; // 10MB
+const UInt16 kMockDataChunkIncrement = 1024; // 1KB
+const char* kMockFilename = "NetworkTests.mock";
+const size_t kMockFileSize = 1024 * 1024 * 10; // 10MB
+
+void getScreenShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h);
+void getCursorPos(SInt32& x, SInt32& y);
+UInt8* newMockData(size_t size);
+void createFile(fstream& file, const char* filename, size_t size);
+
+class NetworkTests : public ::testing::Test
+{
+public:
+ NetworkTests() :
+ m_mockData(NULL),
+ m_mockDataSize(0),
+ m_mockFileSize(0)
+ {
+ m_mockData = newMockData(kMockDataSize);
+ createFile(m_mockFile, kMockFilename, kMockFileSize);
+ }
+
+ ~NetworkTests()
+ {
+ remove(kMockFilename);
+ delete[] m_mockData;
+ }
+
+ void sendMockData(void* eventTarget);
+
+ void sendToClient_mockData_handleClientConnected(const Event&, void* vlistener);
+ void sendToClient_mockData_fileRecieveCompleted(const Event&, void*);
+
+ void sendToClient_mockFile_handleClientConnected(const Event&, void* vlistener);
+ void sendToClient_mockFile_fileRecieveCompleted(const Event& event, void*);
+
+ void sendToServer_mockData_handleClientConnected(const Event&, void* vlistener);
+ void sendToServer_mockData_fileRecieveCompleted(const Event& event, void*);
+
+ void sendToServer_mockFile_handleClientConnected(const Event&, void* vlistener);
+ void sendToServer_mockFile_fileRecieveCompleted(const Event& event, void*);
+
+public:
+ TestEventQueue m_events;
+ UInt8* m_mockData;
+ size_t m_mockDataSize;
+ fstream m_mockFile;
+ size_t m_mockFileSize;
+};
+
+TEST_F(NetworkTests, sendToClient_mockData)
+{
+ // server and client
+ NetworkAddress serverAddress(TEST_HOST, TEST_PORT);
+
+ serverAddress.resolve();
+
+ // server
+ SocketMultiplexer serverSocketMultiplexer;
+ TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer);
+ ClientListener listener(serverAddress, serverSocketFactory, &m_events, false);
+ NiceMock<MockScreen> serverScreen;
+ NiceMock<MockPrimaryClient> primaryClient;
+ NiceMock<MockConfig> serverConfig;
+ NiceMock<MockInputFilter> serverInputFilter;
+
+ m_events.adoptHandler(
+ m_events.forClientListener().connected(), &listener,
+ new TMethodEventJob<NetworkTests>(
+ this, &NetworkTests::sendToClient_mockData_handleClientConnected, &listener));
+
+ ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true));
+ ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter));
+
+ ServerArgs serverArgs;
+ serverArgs.m_enableDragDrop = true;
+ Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs);
+ server.m_mock = true;
+ listener.setServer(&server);
+
+ // client
+ NiceMock<MockScreen> clientScreen;
+ SocketMultiplexer clientSocketMultiplexer;
+ TCPSocketFactory* clientSocketFactory = new TCPSocketFactory(&m_events, &clientSocketMultiplexer);
+
+ ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
+ ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
+
+
+ ClientArgs clientArgs;
+ clientArgs.m_enableDragDrop = true;
+ clientArgs.m_enableCrypto = false;
+ Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs);
+
+ m_events.adoptHandler(
+ m_events.forFile().fileRecieveCompleted(), &client,
+ new TMethodEventJob<NetworkTests>(
+ this, &NetworkTests::sendToClient_mockData_fileRecieveCompleted));
+
+ client.connect();
+
+ m_events.initQuitTimeout(10);
+ m_events.loop();
+ m_events.removeHandler(m_events.forClientListener().connected(), &listener);
+ m_events.removeHandler(m_events.forFile().fileRecieveCompleted(), &client);
+ m_events.cleanupQuitTimeout();
+}
+
+TEST_F(NetworkTests, sendToClient_mockFile)
+{
+ // server and client
+ NetworkAddress serverAddress(TEST_HOST, TEST_PORT);
+
+ serverAddress.resolve();
+
+ // server
+ SocketMultiplexer serverSocketMultiplexer;
+ TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer);
+ ClientListener listener(serverAddress, serverSocketFactory, &m_events, false);
+ NiceMock<MockScreen> serverScreen;
+ NiceMock<MockPrimaryClient> primaryClient;
+ NiceMock<MockConfig> serverConfig;
+ NiceMock<MockInputFilter> serverInputFilter;
+
+ m_events.adoptHandler(
+ m_events.forClientListener().connected(), &listener,
+ new TMethodEventJob<NetworkTests>(
+ this, &NetworkTests::sendToClient_mockFile_handleClientConnected, &listener));
+
+ ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true));
+ ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter));
+
+ ServerArgs serverArgs;
+ serverArgs.m_enableDragDrop = true;
+ Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs);
+ server.m_mock = true;
+ listener.setServer(&server);
+
+ // client
+ NiceMock<MockScreen> clientScreen;
+ SocketMultiplexer clientSocketMultiplexer;
+ TCPSocketFactory* clientSocketFactory = new TCPSocketFactory(&m_events, &clientSocketMultiplexer);
+
+ ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
+ ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
+
+
+ ClientArgs clientArgs;
+ clientArgs.m_enableDragDrop = true;
+ clientArgs.m_enableCrypto = false;
+ Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs);
+
+ m_events.adoptHandler(
+ m_events.forFile().fileRecieveCompleted(), &client,
+ new TMethodEventJob<NetworkTests>(
+ this, &NetworkTests::sendToClient_mockFile_fileRecieveCompleted));
+
+ client.connect();
+
+ m_events.initQuitTimeout(10);
+ m_events.loop();
+ m_events.removeHandler(m_events.forClientListener().connected(), &listener);
+ m_events.removeHandler(m_events.forFile().fileRecieveCompleted(), &client);
+ m_events.cleanupQuitTimeout();
+}
+
+TEST_F(NetworkTests, sendToServer_mockData)
+{
+ // server and client
+ NetworkAddress serverAddress(TEST_HOST, TEST_PORT);
+ serverAddress.resolve();
+
+ // server
+ SocketMultiplexer serverSocketMultiplexer;
+ TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer);
+ ClientListener listener(serverAddress, serverSocketFactory, &m_events, false);
+ NiceMock<MockScreen> serverScreen;
+ NiceMock<MockPrimaryClient> primaryClient;
+ NiceMock<MockConfig> serverConfig;
+ NiceMock<MockInputFilter> serverInputFilter;
+
+ ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true));
+ ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter));
+
+ ServerArgs serverArgs;
+ serverArgs.m_enableDragDrop = true;
+ Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs);
+ server.m_mock = true;
+ listener.setServer(&server);
+
+ // client
+ NiceMock<MockScreen> clientScreen;
+ SocketMultiplexer clientSocketMultiplexer;
+ TCPSocketFactory* clientSocketFactory = new TCPSocketFactory(&m_events, &clientSocketMultiplexer);
+
+ ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
+ ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
+
+ ClientArgs clientArgs;
+ clientArgs.m_enableDragDrop = true;
+ clientArgs.m_enableCrypto = false;
+ Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs);
+
+ m_events.adoptHandler(
+ m_events.forClientListener().connected(), &listener,
+ new TMethodEventJob<NetworkTests>(
+ this, &NetworkTests::sendToServer_mockData_handleClientConnected, &client));
+
+ m_events.adoptHandler(
+ m_events.forFile().fileRecieveCompleted(), &server,
+ new TMethodEventJob<NetworkTests>(
+ this, &NetworkTests::sendToServer_mockData_fileRecieveCompleted));
+
+ client.connect();
+
+ m_events.initQuitTimeout(10);
+ m_events.loop();
+ m_events.removeHandler(m_events.forClientListener().connected(), &listener);
+ m_events.removeHandler(m_events.forFile().fileRecieveCompleted(), &server);
+ m_events.cleanupQuitTimeout();
+}
+
+TEST_F(NetworkTests, sendToServer_mockFile)
+{
+ // server and client
+ NetworkAddress serverAddress(TEST_HOST, TEST_PORT);
+
+ serverAddress.resolve();
+
+ // server
+ SocketMultiplexer serverSocketMultiplexer;
+ TCPSocketFactory* serverSocketFactory = new TCPSocketFactory(&m_events, &serverSocketMultiplexer);
+ ClientListener listener(serverAddress, serverSocketFactory, &m_events, false);
+ NiceMock<MockScreen> serverScreen;
+ NiceMock<MockPrimaryClient> primaryClient;
+ NiceMock<MockConfig> serverConfig;
+ NiceMock<MockInputFilter> serverInputFilter;
+
+ ON_CALL(serverConfig, isScreen(_)).WillByDefault(Return(true));
+ ON_CALL(serverConfig, getInputFilter()).WillByDefault(Return(&serverInputFilter));
+
+ ServerArgs serverArgs;
+ serverArgs.m_enableDragDrop = true;
+ Server server(serverConfig, &primaryClient, &serverScreen, &m_events, serverArgs);
+ server.m_mock = true;
+ listener.setServer(&server);
+
+ // client
+ NiceMock<MockScreen> clientScreen;
+ SocketMultiplexer clientSocketMultiplexer;
+ TCPSocketFactory* clientSocketFactory = new TCPSocketFactory(&m_events, &clientSocketMultiplexer);
+
+ ON_CALL(clientScreen, getShape(_, _, _, _)).WillByDefault(Invoke(getScreenShape));
+ ON_CALL(clientScreen, getCursorPos(_, _)).WillByDefault(Invoke(getCursorPos));
+
+ ClientArgs clientArgs;
+ clientArgs.m_enableDragDrop = true;
+ clientArgs.m_enableCrypto = false;
+ Client client(&m_events, "stub", serverAddress, clientSocketFactory, &clientScreen, clientArgs);
+
+ m_events.adoptHandler(
+ m_events.forClientListener().connected(), &listener,
+ new TMethodEventJob<NetworkTests>(
+ this, &NetworkTests::sendToServer_mockFile_handleClientConnected, &client));
+
+ m_events.adoptHandler(
+ m_events.forFile().fileRecieveCompleted(), &server,
+ new TMethodEventJob<NetworkTests>(
+ this, &NetworkTests::sendToServer_mockFile_fileRecieveCompleted));
+
+ client.connect();
+
+ m_events.initQuitTimeout(10);
+ m_events.loop();
+ m_events.removeHandler(m_events.forClientListener().connected(), &listener);
+ m_events.removeHandler(m_events.forFile().fileRecieveCompleted(), &server);
+ m_events.cleanupQuitTimeout();
+}
+
+void
+NetworkTests::sendToClient_mockData_handleClientConnected(const Event&, void* vlistener)
+{
+ ClientListener* listener = static_cast<ClientListener*>(vlistener);
+ Server* server = listener->getServer();
+
+ ClientProxy* client = listener->getNextClient();
+ if (client == NULL) {
+ throw runtime_error("client is null");
+ }
+
+ BaseClientProxy* bcp = client;
+ server->adoptClient(bcp);
+ server->setActive(bcp);
+
+ sendMockData(server);
+}
+
+void
+NetworkTests::sendToClient_mockData_fileRecieveCompleted(const Event& event, void*)
+{
+ Client* client = static_cast<Client*>(event.getTarget());
+ EXPECT_TRUE(client->isReceivedFileSizeValid());
+
+ m_events.raiseQuitEvent();
+}
+
+void
+NetworkTests::sendToClient_mockFile_handleClientConnected(const Event&, void* vlistener)
+{
+ ClientListener* listener = static_cast<ClientListener*>(vlistener);
+ Server* server = listener->getServer();
+
+ ClientProxy* client = listener->getNextClient();
+ if (client == NULL) {
+ throw runtime_error("client is null");
+ }
+
+ BaseClientProxy* bcp = client;
+ server->adoptClient(bcp);
+ server->setActive(bcp);
+
+ server->sendFileToClient(kMockFilename);
+}
+
+void
+NetworkTests::sendToClient_mockFile_fileRecieveCompleted(const Event& event, void*)
+{
+ Client* client = static_cast<Client*>(event.getTarget());
+ EXPECT_TRUE(client->isReceivedFileSizeValid());
+
+ m_events.raiseQuitEvent();
+}
+
+void
+NetworkTests::sendToServer_mockData_handleClientConnected(const Event&, void* vclient)
+{
+ Client* client = static_cast<Client*>(vclient);
+ sendMockData(client);
+}
+
+void
+NetworkTests::sendToServer_mockData_fileRecieveCompleted(const Event& event, void*)
+{
+ Server* server = static_cast<Server*>(event.getTarget());
+ EXPECT_TRUE(server->isReceivedFileSizeValid());
+
+ m_events.raiseQuitEvent();
+}
+
+void
+NetworkTests::sendToServer_mockFile_handleClientConnected(const Event&, void* vclient)
+{
+ Client* client = static_cast<Client*>(vclient);
+ client->sendFileToServer(kMockFilename);
+}
+
+void
+NetworkTests::sendToServer_mockFile_fileRecieveCompleted(const Event& event, void*)
+{
+ Server* server = static_cast<Server*>(event.getTarget());
+ EXPECT_TRUE(server->isReceivedFileSizeValid());
+
+ m_events.raiseQuitEvent();
+}
+
+void
+NetworkTests::sendMockData(void* eventTarget)
+{
+ // send first message (file size)
+ String size = barrier::string::sizeTypeToString(kMockDataSize);
+ FileChunk* sizeMessage = FileChunk::start(size);
+
+ m_events.addEvent(Event(m_events.forFile().fileChunkSending(), eventTarget, sizeMessage));
+
+ // send chunk messages with incrementing chunk size
+ size_t lastSize = 0;
+ size_t sentLength = 0;
+ while (true) {
+ size_t dataSize = lastSize + kMockDataChunkIncrement;
+
+ // make sure we don't read too much from the mock data.
+ if (sentLength + dataSize > kMockDataSize) {
+ dataSize = kMockDataSize - sentLength;
+ }
+
+ // first byte is the chunk mark, last is \0
+ FileChunk* chunk = FileChunk::data(m_mockData, dataSize);
+ m_events.addEvent(Event(m_events.forFile().fileChunkSending(), eventTarget, chunk));
+
+ sentLength += dataSize;
+ lastSize = dataSize;
+
+ if (sentLength == kMockDataSize) {
+ break;
+ }
+
+ }
+
+ // send last message
+ FileChunk* transferFinished = FileChunk::end();
+ m_events.addEvent(Event(m_events.forFile().fileChunkSending(), eventTarget, transferFinished));
+}
+
+UInt8*
+newMockData(size_t size)
+{
+ UInt8* buffer = new UInt8[size];
+
+ UInt8* data = buffer;
+ const UInt8 head[] = "mock head... ";
+ size_t headSize = sizeof(head) - 1;
+ const UInt8 tail[] = "... mock tail";
+ size_t tailSize = sizeof(tail) - 1;
+ const UInt8 barrierRocks[] = "barrier\0 rocks! ";
+ size_t barrierRocksSize = sizeof(barrierRocks) - 1;
+
+ memcpy(data, head, headSize);
+ data += headSize;
+
+ size_t times = (size - headSize - tailSize) / barrierRocksSize;
+ for (size_t i = 0; i < times; ++i) {
+ memcpy(data, barrierRocks, barrierRocksSize);
+ data += barrierRocksSize;
+ }
+
+ size_t remainder = (size - headSize - tailSize) % barrierRocksSize;
+ if (remainder != 0) {
+ memset(data, '.', remainder);
+ data += remainder;
+ }
+
+ memcpy(data, tail, tailSize);
+ return buffer;
+}
+
+void
+createFile(fstream& file, const char* filename, size_t size)
+{
+ UInt8* buffer = newMockData(size);
+
+ file.open(filename, ios::out | ios::binary);
+ if (!file.is_open()) {
+ throw runtime_error("file not open");
+ }
+
+ file.write(reinterpret_cast<char*>(buffer), size);
+ file.close();
+
+ delete[] buffer;
+}
+
+void
+getScreenShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h)
+{
+ x = 0;
+ y = 0;
+ w = 1;
+ h = 1;
+}
+
+void
+getCursorPos(SInt32& x, SInt32& y)
+{
+ x = 0;
+ y = 0;
+}
+
+#endif // WINAPI_CARBON
diff --git a/src/test/integtests/platform/MSWindowsClipboardTests.cpp b/src/test/integtests/platform/MSWindowsClipboardTests.cpp
new file mode 100644
index 0000000..f9d09d1
--- /dev/null
+++ b/src/test/integtests/platform/MSWindowsClipboardTests.cpp
@@ -0,0 +1,229 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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 "platform/MSWindowsClipboard.h"
+#include "platform/IMSWindowsClipboardFacade.h"
+
+#include "test/global/gmock.h"
+#include "test/global/gtest.h"
+
+class MSWindowsClipboardTests : public ::testing::Test
+{
+protected:
+ virtual void SetUp()
+ {
+ emptyClipboard();
+ }
+
+ virtual void TearDown()
+ {
+ emptyClipboard();
+ }
+
+private:
+ void emptyClipboard()
+ {
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+ clipboard.empty();
+ }
+};
+
+class MockFacade : public IMSWindowsClipboardFacade
+{
+public:
+ MOCK_METHOD2(write, void(HANDLE, UINT));
+};
+
+TEST_F(MSWindowsClipboardTests, emptyUnowned_openCalled_returnsTrue)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+
+ bool actual = clipboard.emptyUnowned();
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST_F(MSWindowsClipboardTests, empty_openCalled_returnsTrue)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+
+ bool actual = clipboard.empty();
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST_F(MSWindowsClipboardTests, empty_singleFormat_hasReturnsFalse)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+ clipboard.add(MSWindowsClipboard::kText, "barrier rocks!");
+
+ clipboard.empty();
+
+ bool actual = clipboard.has(MSWindowsClipboard::kText);
+ EXPECT_EQ(false, actual);
+}
+
+TEST_F(MSWindowsClipboardTests, add_newValue_valueWasStored)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ String actual = clipboard.get(IClipboard::kText);
+ EXPECT_EQ("barrier rocks!", actual);
+}
+
+TEST_F(MSWindowsClipboardTests, add_newValue_writeWasCalled)
+{
+ MockFacade facade;
+ EXPECT_CALL(facade, write(testing::_, testing::_));
+
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.setFacade(facade);
+ clipboard.open(0);
+
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+}
+
+TEST_F(MSWindowsClipboardTests, add_replaceValue_valueWasReplaced)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+ clipboard.add(IClipboard::kText, "maxivista sucks"); // haha, just kidding.
+
+ String actual = clipboard.get(IClipboard::kText);
+ EXPECT_EQ("maxivista sucks", actual);
+}
+
+TEST_F(MSWindowsClipboardTests, open_timeIsZero_returnsTrue)
+{
+ MSWindowsClipboard clipboard(NULL);
+
+ bool actual = clipboard.open(0);
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST_F(MSWindowsClipboardTests, open_timeIsOne_returnsTrue)
+{
+ MSWindowsClipboard clipboard(NULL);
+
+ bool actual = clipboard.open(1);
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST_F(MSWindowsClipboardTests, close_isOpen_noErrors)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+
+ clipboard.close();
+
+ // can't assert anything
+}
+
+// looks like this test may fail intermittently:
+// * http://buildbot.symless.com:8000/builders/trunk-win32/builds/246/steps/shell_3/logs/stdio
+/*TEST_F(MSWindowsClipboardTests, getTime_openWithNoEmpty_returnsOne)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(1);
+
+ MSWindowsClipboard::Time actual = clipboard.getTime();
+
+ // this behavior is different to that of Clipboard which only
+ // returns the value passed into open(t) after empty() is called.
+ EXPECT_EQ(1, actual);
+}*/
+
+// this also fails intermittently:
+// http://buildbot.symless.com:8000/builders/trunk-win32/builds/266/steps/shell_3/logs/stdio
+/*TEST_F(MSWindowsClipboardTests, getTime_openAndEmpty_returnsOne)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(1);
+ clipboard.empty();
+
+ MSWindowsClipboard::Time actual = clipboard.getTime();
+
+ EXPECT_EQ(1, actual);
+}*/
+
+TEST_F(MSWindowsClipboardTests, has_withFormatAdded_returnsTrue)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+ clipboard.empty();
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ bool actual = clipboard.has(IClipboard::kText);
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST_F(MSWindowsClipboardTests, has_withNoFormats_returnsFalse)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+ clipboard.empty();
+
+ bool actual = clipboard.has(IClipboard::kText);
+
+ EXPECT_EQ(false, actual);
+}
+
+TEST_F(MSWindowsClipboardTests, get_withNoFormats_returnsEmpty)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+ clipboard.empty();
+
+ String actual = clipboard.get(IClipboard::kText);
+
+ EXPECT_EQ("", actual);
+}
+
+TEST_F(MSWindowsClipboardTests, get_withFormatAdded_returnsExpected)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+ clipboard.empty();
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ String actual = clipboard.get(IClipboard::kText);
+
+ EXPECT_EQ("barrier rocks!", actual);
+}
+
+TEST_F(MSWindowsClipboardTests, isOwnedByBarrier_defaultState_noError)
+{
+ MSWindowsClipboard clipboard(NULL);
+ clipboard.open(0);
+
+ bool actual = clipboard.isOwnedByBarrier();
+
+ EXPECT_EQ(true, actual);
+}
diff --git a/src/test/integtests/platform/MSWindowsKeyStateTests.cpp b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp
new file mode 100644
index 0000000..f3f9e32
--- /dev/null
+++ b/src/test/integtests/platform/MSWindowsKeyStateTests.cpp
@@ -0,0 +1,144 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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/>.
+ */
+
+#define TEST_ENV
+
+#include "test/mock/barrier/MockEventQueue.h"
+#include "test/mock/barrier/MockKeyMap.h"
+#include "platform/MSWindowsKeyState.h"
+#include "platform/MSWindowsDesks.h"
+#include "platform/MSWindowsScreen.h"
+#include "platform/MSWindowsScreenSaver.h"
+#include "base/TMethodJob.h"
+
+#include "test/global/gtest.h"
+#include "test/global/gmock.h"
+
+// wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code
+#define BARRIER_MSG_FAKE_KEY BARRIER_HOOK_LAST_MSG + 4
+
+using ::testing::_;
+using ::testing::NiceMock;
+
+class MSWindowsKeyStateTests : public ::testing::Test
+{
+protected:
+ virtual void SetUp()
+ {
+ m_hook.loadLibrary();
+ m_screensaver = new MSWindowsScreenSaver();
+ }
+
+ virtual void TearDown()
+ {
+ delete m_screensaver;
+ }
+
+ MSWindowsDesks* newDesks(IEventQueue* eventQueue)
+ {
+ return new MSWindowsDesks(
+ true, false, m_screensaver, eventQueue,
+ new TMethodJob<MSWindowsKeyStateTests>(
+ this, &MSWindowsKeyStateTests::updateKeysCB), false);
+ }
+
+ void* getEventTarget() const
+ {
+ return const_cast<MSWindowsKeyStateTests*>(this);
+ }
+
+private:
+ void updateKeysCB(void*) { }
+ IScreenSaver* m_screensaver;
+ MSWindowsHook m_hook;
+};
+
+TEST_F(MSWindowsKeyStateTests, disable_eventQueueNotUsed)
+{
+ NiceMock<MockEventQueue> eventQueue;
+ MSWindowsDesks* desks = newDesks(&eventQueue);
+ MockKeyMap keyMap;
+ MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap);
+
+ EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(0);
+
+ keyState.disable();
+ delete desks;
+}
+
+TEST_F(MSWindowsKeyStateTests, testAutoRepeat_noRepeatAndButtonIsZero_resultIsTrue)
+{
+ NiceMock<MockEventQueue> eventQueue;
+ MSWindowsDesks* desks = newDesks(&eventQueue);
+ MockKeyMap keyMap;
+ MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap);
+ keyState.setLastDown(1);
+
+ bool actual = keyState.testAutoRepeat(true, false, 1);
+
+ ASSERT_TRUE(actual);
+ delete desks;
+}
+
+TEST_F(MSWindowsKeyStateTests, testAutoRepeat_pressFalse_lastDownIsZero)
+{
+ NiceMock<MockEventQueue> eventQueue;
+ MSWindowsDesks* desks = newDesks(&eventQueue);
+ MockKeyMap keyMap;
+ MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap);
+ keyState.setLastDown(1);
+
+ keyState.testAutoRepeat(false, false, 1);
+
+ ASSERT_EQ(0, keyState.getLastDown());
+ delete desks;
+}
+
+TEST_F(MSWindowsKeyStateTests, saveModifiers_noModifiers_savedModifiers0)
+{
+ NiceMock<MockEventQueue> eventQueue;
+ MSWindowsDesks* desks = newDesks(&eventQueue);
+ MockKeyMap keyMap;
+ MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap);
+
+ keyState.saveModifiers();
+
+ ASSERT_EQ(0, keyState.getSavedModifiers());
+ delete desks;
+}
+
+TEST_F(MSWindowsKeyStateTests, testKoreanLocale_inputModeKey_resultCorrectKeyID)
+{
+ NiceMock<MockEventQueue> eventQueue;
+ MSWindowsDesks* desks = newDesks(&eventQueue);
+ MockKeyMap keyMap;
+ MSWindowsKeyState keyState(desks, getEventTarget(), &eventQueue, keyMap);
+
+ keyState.setKeyLayout((HKL)0x00000412u); // for ko-KR local ID
+ ASSERT_EQ(0xEF31, keyState.getKeyID(0x15u, 0x1f2u)); // VK_HANGUL from Hangul key
+ ASSERT_EQ(0xEF34, keyState.getKeyID(0x19u, 0x1f1u)); // VK_HANJA from Hanja key
+ ASSERT_EQ(0xEF31, keyState.getKeyID(0x15u, 0x11du)); // VK_HANGUL from R-Alt key
+ ASSERT_EQ(0xEF34, keyState.getKeyID(0x19u, 0x138u)); // VK_HANJA from R-Ctrl key
+
+ keyState.setKeyLayout((HKL)0x00000411); // for ja-jp locale ID
+ ASSERT_EQ(0xEF26, keyState.getKeyID(0x15u, 0x1du)); // VK_KANA
+ ASSERT_EQ(0xEF2A, keyState.getKeyID(0x19u, 0x38u)); // VK_KANJI
+
+ delete desks;
+}
+
diff --git a/src/test/integtests/platform/OSXClipboardTests.cpp b/src/test/integtests/platform/OSXClipboardTests.cpp
new file mode 100644
index 0000000..45b73bd
--- /dev/null
+++ b/src/test/integtests/platform/OSXClipboardTests.cpp
@@ -0,0 +1,164 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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 "platform/OSXClipboard.h"
+
+#include "test/global/gtest.h"
+#include <iostream>
+
+TEST(OSXClipboardTests, empty_openCalled_returnsTrue)
+{
+ OSXClipboard clipboard;
+ clipboard.open(0);
+
+ bool actual = clipboard.empty();
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST(OSXClipboardTests, empty_singleFormat_hasReturnsFalse)
+{
+ OSXClipboard clipboard;
+ clipboard.open(0);
+ clipboard.add(OSXClipboard::kText, "barrier rocks!");
+
+ clipboard.empty();
+
+ bool actual = clipboard.has(OSXClipboard::kText);
+ EXPECT_EQ(false, actual);
+}
+
+TEST(OSXClipboardTests, add_newValue_valueWasStored)
+{
+ OSXClipboard clipboard;
+ clipboard.open(0);
+
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ String actual = clipboard.get(IClipboard::kText);
+ EXPECT_EQ("barrier rocks!", actual);
+}
+
+TEST(OSXClipboardTests, add_replaceValue_valueWasReplaced)
+{
+ OSXClipboard clipboard;
+ clipboard.open(0);
+
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+ clipboard.add(IClipboard::kText, "maxivista sucks"); // haha, just kidding.
+
+ String actual = clipboard.get(IClipboard::kText);
+ EXPECT_EQ("maxivista sucks", actual);
+}
+
+TEST(OSXClipboardTests, open_timeIsZero_returnsTrue)
+{
+ OSXClipboard clipboard;
+
+ bool actual = clipboard.open(0);
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST(OSXClipboardTests, open_timeIsOne_returnsTrue)
+{
+ OSXClipboard clipboard;
+
+ bool actual = clipboard.open(1);
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST(OSXClipboardTests, close_isOpen_noErrors)
+{
+ OSXClipboard clipboard;
+ clipboard.open(0);
+
+ clipboard.close();
+
+ // can't assert anything
+}
+
+TEST(OSXClipboardTests, getTime_openWithNoEmpty_returnsOne)
+{
+ OSXClipboard clipboard;
+ clipboard.open(1);
+
+ OSXClipboard::Time actual = clipboard.getTime();
+
+ // this behavior is different to that of Clipboard which only
+ // returns the value passed into open(t) after empty() is called.
+ EXPECT_EQ((UInt32)1, actual);
+}
+
+TEST(OSXClipboardTests, getTime_openAndEmpty_returnsOne)
+{
+ OSXClipboard clipboard;
+ clipboard.open(1);
+ clipboard.empty();
+
+ OSXClipboard::Time actual = clipboard.getTime();
+
+ EXPECT_EQ((UInt32)1, actual);
+}
+
+TEST(OSXClipboardTests, has_withFormatAdded_returnsTrue)
+{
+ OSXClipboard clipboard;
+ clipboard.open(0);
+ clipboard.empty();
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ bool actual = clipboard.has(IClipboard::kText);
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST(OSXClipboardTests, has_withNoFormats_returnsFalse)
+{
+ OSXClipboard clipboard;
+ clipboard.open(0);
+ clipboard.empty();
+
+ bool actual = clipboard.has(IClipboard::kText);
+
+ EXPECT_EQ(false, actual);
+}
+
+TEST(OSXClipboardTests, get_withNoFormats_returnsEmpty)
+{
+ OSXClipboard clipboard;
+ clipboard.open(0);
+ clipboard.empty();
+
+ String actual = clipboard.get(IClipboard::kText);
+
+ EXPECT_EQ("", actual);
+}
+
+TEST(OSXClipboardTests, get_withFormatAdded_returnsExpected)
+{
+ OSXClipboard clipboard;
+ clipboard.open(0);
+ clipboard.empty();
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ String actual = clipboard.get(IClipboard::kText);
+
+ EXPECT_EQ("barrier rocks!", actual);
+}
diff --git a/src/test/integtests/platform/OSXKeyStateTests.cpp b/src/test/integtests/platform/OSXKeyStateTests.cpp
new file mode 100644
index 0000000..4957aaa
--- /dev/null
+++ b/src/test/integtests/platform/OSXKeyStateTests.cpp
@@ -0,0 +1,119 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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/mock/barrier/MockKeyMap.h"
+#include "test/mock/barrier/MockEventQueue.h"
+#include "platform/OSXKeyState.h"
+#include "base/Log.h"
+
+#include "test/global/gtest.h"
+#include "test/global/gmock.h"
+
+#define SHIFT_ID_L kKeyShift_L
+#define SHIFT_ID_R kKeyShift_R
+#define SHIFT_BUTTON 57
+#define A_CHAR_ID 0x00000061
+#define A_CHAR_BUTTON 001
+
+class OSXKeyStateTests : public ::testing::Test {
+public:
+ static bool isKeyPressed(const OSXKeyState& keyState, KeyButton button);
+};
+
+// fakeAndPoll_shift seems to always fail on osx10.6
+#if __MAC_OS_X_VERSION_MIN_REQUIRED > 1060
+
+TEST_F(OSXKeyStateTests, fakeAndPoll_shift)
+{
+ barrier::KeyMap keyMap;
+ MockEventQueue eventQueue;
+ OSXKeyState keyState(&eventQueue, keyMap);
+ keyState.updateKeyMap();
+
+ keyState.fakeKeyDown(SHIFT_ID_L, 0, 1);
+ EXPECT_TRUE(isKeyPressed(keyState, SHIFT_BUTTON));
+
+ keyState.fakeKeyUp(1);
+ EXPECT_TRUE(!isKeyPressed(keyState, SHIFT_BUTTON));
+
+ keyState.fakeKeyDown(SHIFT_ID_R, 0, 2);
+ EXPECT_TRUE(isKeyPressed(keyState, SHIFT_BUTTON));
+
+ keyState.fakeKeyUp(2);
+ EXPECT_TRUE(!isKeyPressed(keyState, SHIFT_BUTTON));
+}
+
+TEST_F(OSXKeyStateTests, fakeAndPoll_charKey)
+{
+ barrier::KeyMap keyMap;
+ MockEventQueue eventQueue;
+ OSXKeyState keyState(&eventQueue, keyMap);
+ keyState.updateKeyMap();
+
+ keyState.fakeKeyDown(A_CHAR_ID, 0, 1);
+ EXPECT_TRUE(isKeyPressed(keyState, A_CHAR_BUTTON));
+
+ keyState.fakeKeyUp(1);
+ EXPECT_TRUE(!isKeyPressed(keyState, A_CHAR_BUTTON));
+
+ // 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.fakeKeyUp(2);
+}
+
+TEST_F(OSXKeyStateTests, fakeAndPoll_charKeyAndModifier)
+{
+ barrier::KeyMap keyMap;
+ MockEventQueue eventQueue;
+ OSXKeyState keyState(&eventQueue, keyMap);
+ keyState.updateKeyMap();
+
+ keyState.fakeKeyDown(A_CHAR_ID, KeyModifierShift, 1);
+ EXPECT_TRUE(isKeyPressed(keyState, A_CHAR_BUTTON));
+
+ keyState.fakeKeyUp(1);
+ EXPECT_TRUE(!isKeyPressed(keyState, A_CHAR_BUTTON));
+
+ // 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.fakeKeyUp(2);
+}
+
+bool
+OSXKeyStateTests::isKeyPressed(const OSXKeyState& keyState, KeyButton button)
+{
+ // HACK: allow os to realize key state changes.
+ ARCH->sleep(.2);
+
+ IKeyState::KeyButtonSet pressed;
+ keyState.pollPressedKeys(pressed);
+
+ IKeyState::KeyButtonSet::const_iterator it;
+ for (it = pressed.begin(); it != pressed.end(); ++it) {
+ LOG((CLOG_DEBUG "checking key %d", *it));
+ if (*it == button) {
+ return true;
+ }
+ }
+ return false;
+}
+
+#endif
+
diff --git a/src/test/integtests/platform/OSXScreenTests.cpp b/src/test/integtests/platform/OSXScreenTests.cpp
new file mode 100644
index 0000000..96beb4d
--- /dev/null
+++ b/src/test/integtests/platform/OSXScreenTests.cpp
@@ -0,0 +1,51 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-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 "platform/OSXScreen.h"
+#include "arch/Arch.h"
+#include "base/EventQueue.h"
+
+#include "test/global/gtest.h"
+
+// disabling these tests - the return value of CGCursorIsVisible is unreliable.
+/*
+TEST(OSXScreenTests, hideCursor_notPrimary)
+{
+ EventQueue queue;
+ OSXScreen screen(true, false);
+
+ screen.hideCursor();
+
+ EXPECT_EQ(false, CGCursorIsVisible());
+
+ // workaround for screen class race condition.
+ ARCH->sleep(.1f);
+}
+
+TEST(OSXScreenTests, showCursor_notPrimary)
+{
+ EventQueue queue;
+ OSXScreen screen(false, false);
+
+ screen.showCursor();
+
+ EXPECT_EQ(true, CGCursorIsVisible());
+
+ // workaround for screen class race condition.
+ ARCH->sleep(.1f);
+}
+*/
diff --git a/src/test/integtests/platform/XWindowsClipboardTests.cpp b/src/test/integtests/platform/XWindowsClipboardTests.cpp
new file mode 100644
index 0000000..652ee5e
--- /dev/null
+++ b/src/test/integtests/platform/XWindowsClipboardTests.cpp
@@ -0,0 +1,157 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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/>.
+ */
+
+// TODO: fix tests - compile error on linux
+#if 0
+
+#include "platform/XWindowsClipboard.h"
+
+#include "test/global/gtest.h"
+#include <iostream>
+
+class CXWindowsClipboardTests : public ::testing::Test
+{
+protected:
+ virtual void
+ SetUp()
+ {
+ m_display = XOpenDisplay(NULL);
+ int screen = DefaultScreen(m_display);
+ Window root = XRootWindow(m_display, screen);
+
+ XSetWindowAttributes attr;
+ attr.do_not_propagate_mask = 0;
+ attr.override_redirect = True;
+ attr.cursor = Cursor();
+
+ m_window = XCreateWindow(
+ m_display, root, 0, 0, 1, 1, 0, 0,
+ InputOnly, CopyFromParent, 0, &attr);
+ }
+
+ virtual void
+ TearDown()
+ {
+ XDestroyWindow(m_display, m_window);
+ XCloseDisplay(m_display);
+ }
+
+ CXWindowsClipboard&
+ createClipboard()
+ {
+ CXWindowsClipboard* clipboard;
+ clipboard = new CXWindowsClipboard(m_display, m_window, 0);
+ clipboard->open(0); // needed to empty the clipboard
+ clipboard->empty(); // needed to own the clipboard
+ return *clipboard;
+ }
+
+ Display* m_display;
+ Window m_window;
+};
+
+TEST_F(CXWindowsClipboardTests, empty_openCalled_returnsTrue)
+{
+ CXWindowsClipboard clipboard = createClipboard();
+
+ bool actual = clipboard.empty();
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST_F(CXWindowsClipboardTests, empty_singleFormat_hasReturnsFalse)
+{
+ CXWindowsClipboard clipboard = createClipboard();
+ clipboard.add(CXWindowsClipboard::kText, "barrier rocks!");
+
+ clipboard.empty();
+
+ bool actual = clipboard.has(CXWindowsClipboard::kText);
+ EXPECT_FALSE(actual);
+}
+
+TEST_F(CXWindowsClipboardTests, add_newValue_valueWasStored)
+{
+ CXWindowsClipboard clipboard = createClipboard();
+
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ String actual = clipboard.get(IClipboard::kText);
+ EXPECT_EQ("barrier rocks!", actual);
+}
+
+TEST_F(CXWindowsClipboardTests, add_replaceValue_valueWasReplaced)
+{
+ CXWindowsClipboard clipboard = createClipboard();
+
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+ clipboard.add(IClipboard::kText, "maxivista sucks"); // haha, just kidding.
+
+ String actual = clipboard.get(IClipboard::kText);
+ EXPECT_EQ("maxivista sucks", actual);
+}
+
+TEST_F(CXWindowsClipboardTests, close_isOpen_noErrors)
+{
+ CXWindowsClipboard clipboard = createClipboard();
+
+ // clipboard opened in createClipboard()
+ clipboard.close();
+
+ // can't assert anything
+}
+
+TEST_F(CXWindowsClipboardTests, has_withFormatAdded_returnsTrue)
+{
+ CXWindowsClipboard clipboard = createClipboard();
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ bool actual = clipboard.has(IClipboard::kText);
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST_F(CXWindowsClipboardTests, has_withNoFormats_returnsFalse)
+{
+ CXWindowsClipboard clipboard = createClipboard();
+
+ bool actual = clipboard.has(IClipboard::kText);
+
+ EXPECT_FALSE(actual);
+}
+
+TEST_F(CXWindowsClipboardTests, get_withNoFormats_returnsEmpty)
+{
+ CXWindowsClipboard clipboard = createClipboard();
+
+ String actual = clipboard.get(IClipboard::kText);
+
+ EXPECT_EQ("", actual);
+}
+
+TEST_F(CXWindowsClipboardTests, get_withFormatAdded_returnsExpected)
+{
+ CXWindowsClipboard clipboard = createClipboard();
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ String actual = clipboard.get(IClipboard::kText);
+
+ EXPECT_EQ("barrier rocks!", actual);
+}
+
+#endif
diff --git a/src/test/integtests/platform/XWindowsKeyStateTests.cpp b/src/test/integtests/platform/XWindowsKeyStateTests.cpp
new file mode 100644
index 0000000..ea8ce44
--- /dev/null
+++ b/src/test/integtests/platform/XWindowsKeyStateTests.cpp
@@ -0,0 +1,246 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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/>.
+ */
+
+#define TEST_ENV
+
+#include "test/mock/barrier/MockKeyMap.h"
+#include "test/mock/barrier/MockEventQueue.h"
+#include "platform/XWindowsKeyState.h"
+#include "base/Log.h"
+
+#define XK_LATIN1
+#define XK_MISCELLANY
+#include <X11/keysymdef.h>
+
+#if HAVE_XKB_EXTENSION
+# include <X11/XKBlib.h>
+#endif
+
+#include "test/global/gtest.h"
+#include "test/global/gmock.h"
+#include <errno.h>
+
+class XWindowsKeyStateTests : public ::testing::Test
+{
+protected:
+ XWindowsKeyStateTests() :
+ m_display(NULL)
+ {
+ }
+
+ ~XWindowsKeyStateTests()
+ {
+ if (m_display != NULL) {
+ LOG((CLOG_DEBUG "closing display"));
+ XCloseDisplay(m_display);
+ }
+ }
+
+ virtual void
+ SetUp()
+ {
+ // open the display only once for the entire test suite
+ if (this->m_display == NULL) {
+ LOG((CLOG_DEBUG "opening display"));
+ this->m_display = XOpenDisplay(NULL);
+
+ ASSERT_TRUE(this->m_display != NULL)
+ << "unable to open display: " << errno;
+ }
+ }
+
+ virtual void
+ TearDown()
+ {
+ }
+
+ Display* m_display;
+};
+
+TEST_F(XWindowsKeyStateTests, setActiveGroup_pollAndSet_groupIsZero)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ keyState.setActiveGroup(XWindowsKeyState::kGroupPollAndSet);
+
+ ASSERT_EQ(0, keyState.group());
+}
+
+TEST_F(XWindowsKeyStateTests, setActiveGroup_poll_groupIsNotSet)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ keyState.setActiveGroup(XWindowsKeyState::kGroupPoll);
+
+ ASSERT_LE(-1, keyState.group());
+}
+
+TEST_F(XWindowsKeyStateTests, setActiveGroup_customGroup_groupWasSet)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ keyState.setActiveGroup(1);
+
+ ASSERT_EQ(1, keyState.group());
+}
+
+TEST_F(XWindowsKeyStateTests, mapModifiersFromX_zeroState_zeroMask)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ int mask = keyState.mapModifiersFromX(0);
+
+ ASSERT_EQ(0, mask);
+}
+
+TEST_F(XWindowsKeyStateTests, mapModifiersToX_zeroMask_resultIsTrue)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ unsigned int modifiers = 0;
+ bool result = keyState.mapModifiersToX(0, modifiers);
+
+ ASSERT_TRUE(result);
+}
+
+TEST_F(XWindowsKeyStateTests, fakeCtrlAltDel_default_returnsFalse)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ bool result = keyState.fakeCtrlAltDel();
+
+ ASSERT_FALSE(result);
+}
+
+TEST_F(XWindowsKeyStateTests, pollActiveModifiers_defaultState_returnsZero)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ KeyModifierMask actual = keyState.pollActiveModifiers();
+
+ ASSERT_EQ(0, actual);
+}
+
+#if 0 // TODO: fix, causes sigsegv
+TEST_F(XWindowsKeyStateTests, pollActiveModifiers_shiftKeyDownThenUp_masksAreCorrect)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ // set mock modifier mapping
+ std::fill(keyState.modifierFromX().begin(), keyState.modifierFromX().end(), 0);
+ keyState.modifierFromX()[ShiftMapIndex] = KeyModifierShift;
+
+ KeyCode key = XKeysymToKeycode(m_display, XK_Shift_L);
+
+ // fake shift key down (without using barrier)
+ XTestFakeKeyEvent(m_display, key, true, CurrentTime);
+
+ // function under test (1st call)
+ KeyModifierMask modDown = keyState.pollActiveModifiers();
+
+ // fake shift key up (without using barrier)
+ XTestFakeKeyEvent(m_display, key, false, CurrentTime);
+
+ // function under test (2nd call)
+ KeyModifierMask modUp = keyState.pollActiveModifiers();
+
+ EXPECT_TRUE((modDown & KeyModifierShift) == KeyModifierShift)
+ << "shift key not in mask - key was not pressed";
+
+ EXPECT_TRUE((modUp & KeyModifierShift) == 0)
+ << "shift key still in mask - make sure no keys are being held down";
+}
+#endif
+
+TEST_F(XWindowsKeyStateTests, pollActiveGroup_defaultState_returnsZero)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ SInt32 actual = keyState.pollActiveGroup();
+
+ ASSERT_EQ(0, actual);
+}
+
+TEST_F(XWindowsKeyStateTests, pollActiveGroup_positiveGroup_returnsGroup)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ keyState.group(3);
+
+ SInt32 actual = keyState.pollActiveGroup();
+
+ ASSERT_EQ(3, actual);
+}
+
+TEST_F(XWindowsKeyStateTests, pollActiveGroup_xkb_areEqual)
+{
+#if HAVE_XKB_EXTENSION
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ XWindowsKeyState keyState(
+ m_display, true, &eventQueue, keyMap);
+
+ // reset the group
+ keyState.group(-1);
+
+ XkbStateRec state;
+
+ // compare pollActiveGroup() with XkbGetState()
+ if (XkbGetState(m_display, XkbUseCoreKbd, &state) == Success) {
+ SInt32 actual = keyState.pollActiveGroup();
+
+ ASSERT_EQ(state.group, actual);
+ }
+ else {
+ FAIL() << "XkbGetState() returned error " << errno;
+ }
+#else
+ SUCCEED() << "Xkb extension not installed";
+#endif
+}
+
diff --git a/src/test/integtests/platform/XWindowsScreenSaverTests.cpp b/src/test/integtests/platform/XWindowsScreenSaverTests.cpp
new file mode 100644
index 0000000..c6a2710
--- /dev/null
+++ b/src/test/integtests/platform/XWindowsScreenSaverTests.cpp
@@ -0,0 +1,48 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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/>.
+ */
+
+// TODO: fix tests
+#if 0
+
+#include "test/mock/barrier/MockEventQueue.h"
+#include "platform/XWindowsScreenSaver.h"
+
+#include "test/global/gtest.h"
+#include <X11/Xlib.h>
+
+using ::testing::_;
+
+// TODO: not working on build machine for some reason
+TEST(CXWindowsScreenSaverTests, activate_defaultScreen_todo)
+{
+ Display* display = XOpenDisplay(":0.0");
+ Window window = DefaultRootWindow(display);
+ MockEventQueue eventQueue;
+ EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(1);
+ CXWindowsScreenSaver screenSaver(
+ display, window, NULL, &eventQueue);
+
+ screenSaver.activate();
+
+ bool isActive = screenSaver.isActive();
+
+ screenSaver.deactivate();
+
+ ASSERT_EQ(true, isActive);
+}
+#endif
diff --git a/src/test/integtests/platform/XWindowsScreenTests.cpp b/src/test/integtests/platform/XWindowsScreenTests.cpp
new file mode 100644
index 0000000..b74599c
--- /dev/null
+++ b/src/test/integtests/platform/XWindowsScreenTests.cpp
@@ -0,0 +1,41 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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/mock/barrier/MockEventQueue.h"
+#include "platform/XWindowsScreen.h"
+
+#include "test/global/gtest.h"
+
+using ::testing::_;
+
+TEST(CXWindowsScreenTests, fakeMouseMove_nonPrimary_getCursorPosValuesCorrect)
+{
+ MockEventQueue eventQueue;
+ EXPECT_CALL(eventQueue, adoptHandler(_, _, _)).Times(2);
+ EXPECT_CALL(eventQueue, adoptBuffer(_)).Times(2);
+ EXPECT_CALL(eventQueue, removeHandler(_, _)).Times(2);
+ XWindowsScreen screen(
+ ":0.0", false, false, 0, &eventQueue);
+
+ screen.fakeMouseMove(10, 20);
+
+ int x, y;
+ screen.getCursorPos(x, y);
+ ASSERT_EQ(10, x);
+ ASSERT_EQ(20, y);
+}
diff --git a/src/test/mock/barrier/MockApp.h b/src/test/mock/barrier/MockApp.h
new file mode 100644
index 0000000..91745d3
--- /dev/null
+++ b/src/test/mock/barrier/MockApp.h
@@ -0,0 +1,44 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#define TEST_ENV
+
+#include "barrier/App.h"
+
+#include "test/global/gmock.h"
+
+class MockApp : public App
+{
+public:
+ MockApp() : App(NULL, NULL, NULL) { }
+
+ MOCK_METHOD0(help, void());
+ MOCK_METHOD0(loadConfig, void());
+ MOCK_METHOD1(loadConfig, bool(const String&));
+ MOCK_CONST_METHOD0(daemonInfo, const char*());
+ MOCK_CONST_METHOD0(daemonName, const char*());
+ MOCK_METHOD2(parseArgs, void(int, const char* const*));
+ MOCK_METHOD0(version, void());
+ MOCK_METHOD2(standardStartup, int(int, char**));
+ MOCK_METHOD4(runInner, int(int, char**, ILogOutputter*, StartupFunc));
+ MOCK_METHOD0(startNode, void());
+ MOCK_METHOD0(mainLoop, int());
+ MOCK_METHOD2(foregroundStartup, int(int, char**));
+ MOCK_METHOD0(createScreen, barrier::Screen*());
+};
diff --git a/src/test/mock/barrier/MockArgParser.h b/src/test/mock/barrier/MockArgParser.h
new file mode 100644
index 0000000..b1dc07c
--- /dev/null
+++ b/src/test/mock/barrier/MockArgParser.h
@@ -0,0 +1,33 @@
+/*
+ * barrier -- 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/>.
+ */
+
+#pragma once
+
+#define TEST_ENV
+
+#include "barrier/ArgParser.h"
+
+#include "test/global/gmock.h"
+
+class MockArgParser : public ArgParser
+{
+public:
+ MockArgParser() : ArgParser(NULL) { }
+
+ MOCK_METHOD3(parseGenericArgs, bool(int, const char* const*, int&));
+ MOCK_METHOD0(checkUnexpectedArgs, bool());
+};
diff --git a/src/test/mock/barrier/MockEventQueue.h b/src/test/mock/barrier/MockEventQueue.h
new file mode 100644
index 0000000..f273736
--- /dev/null
+++ b/src/test/mock/barrier/MockEventQueue.h
@@ -0,0 +1,67 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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/IEventQueue.h"
+
+#include "test/global/gmock.h"
+
+class MockEventQueue : public IEventQueue
+{
+public:
+ MOCK_METHOD0(loop, void());
+ MOCK_METHOD2(newOneShotTimer, EventQueueTimer*(double, void*));
+ MOCK_METHOD2(newTimer, EventQueueTimer*(double, void*));
+ MOCK_METHOD2(getEvent, bool(Event&, double));
+ MOCK_METHOD1(adoptBuffer, void(IEventQueueBuffer*));
+ MOCK_METHOD2(registerTypeOnce, Event::Type(Event::Type&, const char*));
+ MOCK_METHOD1(removeHandlers, void(void*));
+ MOCK_METHOD1(registerType, Event::Type(const char*));
+ MOCK_CONST_METHOD0(isEmpty, bool());
+ MOCK_METHOD3(adoptHandler, void(Event::Type, void*, IEventJob*));
+ MOCK_METHOD1(getTypeName, const char*(Event::Type));
+ MOCK_METHOD1(addEvent, void(const Event&));
+ MOCK_METHOD2(removeHandler, void(Event::Type, void*));
+ MOCK_METHOD1(dispatchEvent, bool(const Event&));
+ MOCK_CONST_METHOD2(getHandler, IEventJob*(Event::Type, void*));
+ MOCK_METHOD1(deleteTimer, void(EventQueueTimer*));
+ MOCK_CONST_METHOD1(getRegisteredType, Event::Type(const String&));
+ MOCK_METHOD0(getSystemTarget, void*());
+ MOCK_METHOD0(forClient, ClientEvents&());
+ MOCK_METHOD0(forIStream, IStreamEvents&());
+ MOCK_METHOD0(forIpcClient, IpcClientEvents&());
+ MOCK_METHOD0(forIpcClientProxy, IpcClientProxyEvents&());
+ MOCK_METHOD0(forIpcServer, IpcServerEvents&());
+ MOCK_METHOD0(forIpcServerProxy, IpcServerProxyEvents&());
+ MOCK_METHOD0(forIDataSocket, IDataSocketEvents&());
+ MOCK_METHOD0(forIListenSocket, IListenSocketEvents&());
+ MOCK_METHOD0(forISocket, ISocketEvents&());
+ MOCK_METHOD0(forOSXScreen, OSXScreenEvents&());
+ MOCK_METHOD0(forClientListener, ClientListenerEvents&());
+ MOCK_METHOD0(forClientProxy, ClientProxyEvents&());
+ MOCK_METHOD0(forClientProxyUnknown, ClientProxyUnknownEvents&());
+ MOCK_METHOD0(forServer, ServerEvents&());
+ MOCK_METHOD0(forServerApp, ServerAppEvents&());
+ MOCK_METHOD0(forIKeyState, IKeyStateEvents&());
+ MOCK_METHOD0(forIPrimaryScreen, IPrimaryScreenEvents&());
+ MOCK_METHOD0(forIScreen, IScreenEvents&());
+ MOCK_METHOD0(forClipboard, ClipboardEvents&());
+ MOCK_METHOD0(forFile, FileEvents&());
+ MOCK_CONST_METHOD0(waitForReady, void());
+};
diff --git a/src/test/mock/barrier/MockKeyMap.h b/src/test/mock/barrier/MockKeyMap.h
new file mode 100644
index 0000000..ef711a5
--- /dev/null
+++ b/src/test/mock/barrier/MockKeyMap.h
@@ -0,0 +1,36 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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 "barrier/KeyMap.h"
+
+#include "test/global/gmock.h"
+
+class MockKeyMap : public barrier::KeyMap
+{
+public:
+ MOCK_METHOD1(swap, void(KeyMap&));
+ MOCK_METHOD0(finish, void());
+ MOCK_METHOD2(foreachKey, void(ForeachKeyCallback, void*));
+ MOCK_METHOD1(addHalfDuplexModifier, void(KeyID));
+ MOCK_CONST_METHOD2(isHalfDuplex, bool(KeyID, KeyButton));
+ MOCK_CONST_METHOD7(mapKey, const KeyMap::KeyItem*(
+ Keystrokes&, KeyID, SInt32, ModifierToKeys&, KeyModifierMask&,
+ KeyModifierMask, bool));
+};
diff --git a/src/test/mock/barrier/MockKeyState.h b/src/test/mock/barrier/MockKeyState.h
new file mode 100644
index 0000000..308e90a
--- /dev/null
+++ b/src/test/mock/barrier/MockKeyState.h
@@ -0,0 +1,57 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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 "barrier/KeyState.h"
+
+#include "test/global/gmock.h"
+
+class MockKeyMap;
+class MockEventQueue;
+
+// NOTE: do not mock methods that are not pure virtual. this mock exists only
+// to provide an implementation of the KeyState abstract class.
+class MockKeyState : public KeyState
+{
+public:
+ MockKeyState(const MockEventQueue& eventQueue) :
+ KeyState((IEventQueue*)&eventQueue)
+ {
+ }
+
+ MockKeyState(const MockEventQueue& eventQueue, const MockKeyMap& keyMap) :
+ KeyState((IEventQueue*)&eventQueue, (barrier::KeyMap&)keyMap)
+ {
+ }
+
+ MOCK_CONST_METHOD0(pollActiveGroup, SInt32());
+ MOCK_CONST_METHOD0(pollActiveModifiers, KeyModifierMask());
+ MOCK_METHOD0(fakeCtrlAltDel, bool());
+ MOCK_METHOD1(getKeyMap, void(barrier::KeyMap&));
+ MOCK_METHOD1(fakeKey, void(const Keystroke&));
+ MOCK_METHOD1(fakeMediaKey, bool(KeyID));
+ MOCK_CONST_METHOD1(pollPressedKeys, void(KeyButtonSet&));
+};
+
+typedef ::testing::NiceMock<MockKeyState> KeyStateImpl;
+
+typedef UInt32 KeyID;
+
+typedef void (*ForeachKeyCallback)(
+ KeyID, SInt32 group, barrier::KeyMap::KeyItem&, void* userData);
diff --git a/src/test/mock/barrier/MockScreen.h b/src/test/mock/barrier/MockScreen.h
new file mode 100644
index 0000000..78c195a
--- /dev/null
+++ b/src/test/mock/barrier/MockScreen.h
@@ -0,0 +1,36 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#define TEST_ENV
+
+#include "barrier/Screen.h"
+
+#include "test/global/gmock.h"
+
+class MockScreen : public barrier::Screen
+{
+public:
+ MockScreen() : barrier::Screen() { }
+ MOCK_METHOD0(disable, void());
+ MOCK_CONST_METHOD4(getShape, void(SInt32&, SInt32&, SInt32&, SInt32&));
+ MOCK_CONST_METHOD2(getCursorPos, void(SInt32&, SInt32&));
+ MOCK_METHOD0(resetOptions, void());
+ MOCK_METHOD1(setOptions, void(const OptionsList&));
+ MOCK_METHOD0(enable, void());
+};
diff --git a/src/test/mock/io/MockStream.h b/src/test/mock/io/MockStream.h
new file mode 100644
index 0000000..bd12739
--- /dev/null
+++ b/src/test/mock/io/MockStream.h
@@ -0,0 +1,44 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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 "io/IStream.h"
+
+#include "test/global/gmock.h"
+
+class IEventQueue;
+
+class MockStream : public barrier::IStream
+{
+public:
+ MockStream() { }
+ MOCK_METHOD0(close, void());
+ MOCK_METHOD2(read, UInt32(void*, UInt32));
+ MOCK_METHOD2(write, void(const void*, UInt32));
+ MOCK_METHOD0(flush, void());
+ MOCK_METHOD0(shutdownInput, void());
+ MOCK_METHOD0(shutdownOutput, void());
+ MOCK_METHOD0(getInputReadyEvent, Event::Type());
+ MOCK_METHOD0(getOutputErrorEvent, Event::Type());
+ MOCK_METHOD0(getInputShutdownEvent, Event::Type());
+ MOCK_METHOD0(getOutputShutdownEvent, Event::Type());
+ MOCK_CONST_METHOD0(getEventTarget, void*());
+ MOCK_CONST_METHOD0(isReady, bool());
+ MOCK_CONST_METHOD0(getSize, UInt32());
+};
diff --git a/src/test/mock/ipc/MockIpcServer.h b/src/test/mock/ipc/MockIpcServer.h
new file mode 100644
index 0000000..4124b41
--- /dev/null
+++ b/src/test/mock/ipc/MockIpcServer.h
@@ -0,0 +1,68 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#pragma once
+
+#include "ipc/IpcServer.h"
+#include "ipc/IpcMessage.h"
+#include "arch/Arch.h"
+
+#include "test/global/gmock.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+
+class IEventQueue;
+
+class MockIpcServer : public IpcServer
+{
+public:
+ MockIpcServer() :
+ m_sendCond(ARCH->newCondVar()),
+ m_sendMutex(ARCH->newMutex()) { }
+
+ ~MockIpcServer() {
+ if (m_sendCond != NULL) {
+ ARCH->closeCondVar(m_sendCond);
+ }
+
+ if (m_sendMutex != NULL) {
+ ARCH->closeMutex(m_sendMutex);
+ }
+ }
+
+ MOCK_METHOD0(listen, void());
+ MOCK_METHOD2(send, void(const IpcMessage&, EIpcClientType));
+ MOCK_CONST_METHOD1(hasClients, bool(EIpcClientType));
+
+ void delegateToFake() {
+ ON_CALL(*this, send(_, _)).WillByDefault(Invoke(this, &MockIpcServer::mockSend));
+ }
+
+ void waitForSend() {
+ ARCH->waitCondVar(m_sendCond, m_sendMutex, 5);
+ }
+
+private:
+ void mockSend(const IpcMessage&, EIpcClientType) {
+ ArchMutexLock lock(m_sendMutex);
+ ARCH->broadcastCondVar(m_sendCond);
+ }
+
+ ArchCond m_sendCond;
+ ArchMutex m_sendMutex;
+};
diff --git a/src/test/mock/server/MockConfig.h b/src/test/mock/server/MockConfig.h
new file mode 100644
index 0000000..4161de0
--- /dev/null
+++ b/src/test/mock/server/MockConfig.h
@@ -0,0 +1,32 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#define TEST_ENV
+
+#include "server/Config.h"
+
+#include "test/global/gmock.h"
+
+class MockConfig : public Config
+{
+public:
+ MockConfig() : Config() { }
+ MOCK_METHOD0(getInputFilter, InputFilter*());
+ MOCK_CONST_METHOD1(isScreen, bool(const String&));
+};
diff --git a/src/test/mock/server/MockInputFilter.h b/src/test/mock/server/MockInputFilter.h
new file mode 100644
index 0000000..edf6de1
--- /dev/null
+++ b/src/test/mock/server/MockInputFilter.h
@@ -0,0 +1,30 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#define TEST_ENV
+
+#include "server/InputFilter.h"
+
+#include "test/global/gmock.h"
+
+class MockInputFilter : public InputFilter
+{
+public:
+ MOCK_METHOD1(setPrimaryClient, void(PrimaryClient*));
+};
diff --git a/src/test/mock/server/MockPrimaryClient.h b/src/test/mock/server/MockPrimaryClient.h
new file mode 100644
index 0000000..80f18a1
--- /dev/null
+++ b/src/test/mock/server/MockPrimaryClient.h
@@ -0,0 +1,41 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#define TEST_ENV
+
+#include "server/PrimaryClient.h"
+#include "base/String.h"
+
+#include "test/global/gmock.h"
+
+class MockPrimaryClient : public PrimaryClient
+{
+public:
+ MOCK_CONST_METHOD0(getEventTarget, void*());
+ MOCK_CONST_METHOD2(getCursorPos, void(SInt32&, SInt32&));
+ MOCK_CONST_METHOD2(setJumpCursorPos, void(SInt32, SInt32));
+ MOCK_METHOD1(reconfigure, void(UInt32));
+ MOCK_METHOD0(resetOptions, void());
+ MOCK_METHOD1(setOptions, void(const OptionsList&));
+ MOCK_METHOD0(enable, void());
+ MOCK_METHOD0(disable, void());
+ MOCK_METHOD2(registerHotKey, UInt32(KeyID, KeyModifierMask));
+ MOCK_CONST_METHOD0(getToggleMask, KeyModifierMask());
+ MOCK_METHOD1(unregisterHotKey, void(UInt32));
+};
diff --git a/src/test/mock/server/MockServer.h b/src/test/mock/server/MockServer.h
new file mode 100644
index 0000000..a45ee08
--- /dev/null
+++ b/src/test/mock/server/MockServer.h
@@ -0,0 +1,32 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2013-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/>.
+ */
+
+#pragma once
+
+#define TEST_ENV
+
+#include "server/Server.h"
+
+#include "test/global/gmock.h"
+
+class IEventQueue;
+
+class MockServer : public Server
+{
+public:
+ MockServer() : Server() { }
+};
diff --git a/src/test/unittests/CMakeLists.txt b/src/test/unittests/CMakeLists.txt
new file mode 100644
index 0000000..3d21a2b
--- /dev/null
+++ b/src/test/unittests/CMakeLists.txt
@@ -0,0 +1,71 @@
+# barrier -- mouse and keyboard sharing utility
+# Copyright (C) 2012-2016 Symless Ltd.
+# Copyright (C) 2009 Nick Bolton
+#
+# 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/>.
+
+file(GLOB_RECURSE headers "*.h")
+file(GLOB_RECURSE sources "*.cpp")
+
+file(GLOB_RECURSE remove_platform "platform/*")
+list(REMOVE_ITEM headers ${remove_platform})
+list(REMOVE_ITEM sources ${remove_platform})
+
+file(GLOB_RECURSE global_headers "../../test/global/*.h")
+file(GLOB_RECURSE global_sources "../../test/global/*.cpp")
+
+list(APPEND headers ${global_headers})
+list(APPEND sources ${global_sources})
+
+file(GLOB_RECURSE mock_headers "../../test/mock/*.h")
+file(GLOB_RECURSE mock_sources "../../test/mock/*.cpp")
+
+list(APPEND headers ${mock_headers})
+list(APPEND sources ${mock_sources})
+
+# platform
+if (WIN32)
+ file(GLOB platform_sources "platform/MSWindows*.cpp")
+ file(GLOB platform_headers "platform/MSWindows*.h")
+elseif (APPLE)
+ file(GLOB platform_sources "platform/OSX*.cpp")
+ file(GLOB platform_headers "platform/OSX*.h")
+elseif (UNIX)
+ file(GLOB platform_sources "platform/XWindows*.cpp")
+ file(GLOB platform_headers "platform/XWindows*.h")
+endif()
+
+list(APPEND sources ${platform_headers})
+list(APPEND headers ${platform_sources})
+
+include_directories(
+ ../../
+ ../../lib/
+ ../../../ext/gtest/include
+ ../../../ext/gmock/include
+ ../../../ext
+)
+
+if (UNIX)
+ include_directories(
+ ../../..
+ )
+endif()
+
+if (BARRIER_ADD_HEADERS)
+ list(APPEND sources ${headers})
+endif()
+
+add_executable(unittests ${sources})
+target_link_libraries(unittests
+ arch base client server common io net platform server barrier mt ipc gtest gmock ${libs} ${OPENSSL_LIBS})
diff --git a/src/test/unittests/Main.cpp b/src/test/unittests/Main.cpp
new file mode 100644
index 0000000..7f0d0fe
--- /dev/null
+++ b/src/test/unittests/Main.cpp
@@ -0,0 +1,50 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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 "arch/Arch.h"
+#include "base/Log.h"
+
+#if SYSAPI_WIN32
+#include "arch/win32/ArchMiscWindows.h"
+#endif
+
+#include "test/global/gtest.h"
+
+int
+main(int argc, char **argv)
+{
+#if SYSAPI_WIN32
+ // HACK: shouldn't be needed, but logging fails without this.
+ ArchMiscWindows::setInstanceWin32(GetModuleHandle(NULL));
+#endif
+
+ Arch arch;
+ arch.init();
+
+ Log log;
+ log.setFilter(kDEBUG4);
+
+ testing::InitGoogleTest(&argc, argv);
+
+ // gtest seems to randomly finish with error codes (e.g. -1, -1073741819)
+ // even when no tests have failed. not sure what causes this, but it
+ // happens on all platforms and keeps leading to false positives.
+ // according to the documentation, 1 is a failure, so we should be
+ // able to trust that code.
+ return (RUN_ALL_TESTS() == 1) ? 1 : 0;
+}
diff --git a/src/test/unittests/barrier/ArgParserTests.cpp b/src/test/unittests/barrier/ArgParserTests.cpp
new file mode 100644
index 0000000..e14877e
--- /dev/null
+++ b/src/test/unittests/barrier/ArgParserTests.cpp
@@ -0,0 +1,207 @@
+/*
+ * barrier -- 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 "barrier/ArgParser.h"
+#include "barrier/ArgsBase.h"
+
+#include "test/global/gtest.h"
+
+TEST(ArgParserTests, isArg_abbreviationsArg_returnTrue)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* argv[argc] = { "stub", "-t" };
+ bool result = ArgParser::isArg(i, argc, argv, "-t", NULL);
+
+ EXPECT_EQ(true, result);
+}
+
+TEST(ArgParserTests, isArg_fullArg_returnTrue)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* argv[argc] = { "stub", "--test" };
+ bool result = ArgParser::isArg(i, argc, argv, NULL, "--test");
+
+ EXPECT_EQ(true, result);
+}
+
+TEST(ArgParserTests, isArg_missingArgs_returnFalse)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* argv[argc] = { "stub", "-t" };
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ bool result = ArgParser::isArg(i, argc, argv, "-t", NULL, 1);
+
+ EXPECT_FALSE(result);
+ EXPECT_EQ(true, argsBase.m_shouldExit);
+}
+
+TEST(ArgParserTests, searchDoubleQuotes_doubleQuotedArg_returnTrue)
+{
+ String command("\"stub\"");
+ size_t left = 0;
+ size_t right = 0;
+
+ bool result = ArgParser::searchDoubleQuotes(command, left, right);
+
+ EXPECT_EQ(true, result);
+ EXPECT_EQ(0, left);
+ EXPECT_EQ(5, right);
+}
+
+TEST(ArgParserTests, searchDoubleQuotes_noDoubleQuotedArg_returnfalse)
+{
+ String command("stub");
+ size_t left = 0;
+ size_t right = 0;
+
+ bool result = ArgParser::searchDoubleQuotes(command, left, right);
+
+ EXPECT_FALSE(result);
+ EXPECT_EQ(0, left);
+ EXPECT_EQ(0, right);
+}
+
+TEST(ArgParserTests, searchDoubleQuotes_oneDoubleQuoteArg_returnfalse)
+{
+ String command("\"stub");
+ size_t left = 0;
+ size_t right = 0;
+
+ bool result = ArgParser::searchDoubleQuotes(command, left, right);
+
+ EXPECT_FALSE(result);
+ EXPECT_EQ(0, left);
+ EXPECT_EQ(0, right);
+}
+
+TEST(ArgParserTests, splitCommandString_oneArg_returnArgv)
+{
+ String command("stub");
+ std::vector<String> argv;
+
+ ArgParser::splitCommandString(command, argv);
+
+ EXPECT_EQ(1, argv.size());
+ EXPECT_EQ("stub", argv.at(0));
+}
+
+TEST(ArgParserTests, splitCommandString_twoArgs_returnArgv)
+{
+ String command("stub1 stub2");
+ std::vector<String> argv;
+
+ ArgParser::splitCommandString(command, argv);
+
+ EXPECT_EQ(2, argv.size());
+ EXPECT_EQ("stub1", argv.at(0));
+ EXPECT_EQ("stub2", argv.at(1));
+}
+
+TEST(ArgParserTests, splitCommandString_doubleQuotedArgs_returnArgv)
+{
+ String command("\"stub1\" stub2 \"stub3\"");
+ std::vector<String> argv;
+
+ ArgParser::splitCommandString(command, argv);
+
+ EXPECT_EQ(3, argv.size());
+ EXPECT_EQ("stub1", argv.at(0));
+ EXPECT_EQ("stub2", argv.at(1));
+ EXPECT_EQ("stub3", argv.at(2));
+}
+
+TEST(ArgParserTests, splitCommandString_spaceDoubleQuotedArgs_returnArgv)
+{
+ String command("\"stub1\" stub2 \"stub3 space\"");
+ std::vector<String> argv;
+
+ ArgParser::splitCommandString(command, argv);
+
+ EXPECT_EQ(3, argv.size());
+ EXPECT_EQ("stub1", argv.at(0));
+ EXPECT_EQ("stub2", argv.at(1));
+ EXPECT_EQ("stub3 space", argv.at(2));
+}
+
+TEST(ArgParserTests, getArgv_stringArray_return2DArray)
+{
+ std::vector<String> argArray;
+ argArray.push_back("stub1");
+ argArray.push_back("stub2");
+ argArray.push_back("stub3 space");
+ const char** argv = ArgParser::getArgv(argArray);
+
+ String row1(argv[0]);
+ String row2(argv[1]);
+ String row3(argv[2]);
+
+ EXPECT_EQ("stub1", row1);
+ EXPECT_EQ("stub2", row2);
+ EXPECT_EQ("stub3 space", row3);
+
+ delete[] argv;
+}
+
+TEST(ArgParserTests, assembleCommand_stringArray_returnCommand)
+{
+ std::vector<String> argArray;
+ argArray.push_back("stub1");
+ argArray.push_back("stub2");
+ String command = ArgParser::assembleCommand(argArray);
+
+ EXPECT_EQ("stub1 stub2", command);
+}
+
+TEST(ArgParserTests, assembleCommand_ignoreSecondArg_returnCommand)
+{
+ std::vector<String> argArray;
+ argArray.push_back("stub1");
+ argArray.push_back("stub2");
+ String command = ArgParser::assembleCommand(argArray, "stub2");
+
+ EXPECT_EQ("stub1", command);
+}
+
+TEST(ArgParserTests, assembleCommand_ignoreSecondArgWithOneParameter_returnCommand)
+{
+ std::vector<String> argArray;
+ argArray.push_back("stub1");
+ argArray.push_back("stub2");
+ argArray.push_back("stub3");
+ argArray.push_back("stub4");
+ String command = ArgParser::assembleCommand(argArray, "stub2", 1);
+
+ EXPECT_EQ("stub1 stub4", command);
+}
+
+TEST(ArgParserTests, assembleCommand_stringArrayWithSpace_returnCommand)
+{
+ std::vector<String> argArray;
+ argArray.push_back("stub1 space");
+ argArray.push_back("stub2");
+ argArray.push_back("stub3 space");
+ String command = ArgParser::assembleCommand(argArray);
+
+ EXPECT_EQ("\"stub1 space\" stub2 \"stub3 space\"", command);
+}
+
diff --git a/src/test/unittests/barrier/ClientArgsParsingTests.cpp b/src/test/unittests/barrier/ClientArgsParsingTests.cpp
new file mode 100644
index 0000000..5a1e7d0
--- /dev/null
+++ b/src/test/unittests/barrier/ClientArgsParsingTests.cpp
@@ -0,0 +1,95 @@
+/*
+ * barrier -- 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 "barrier/ArgParser.h"
+#include "barrier/ClientArgs.h"
+#include "test/mock/barrier/MockArgParser.h"
+
+#include "test/global/gtest.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+
+bool
+client_stubParseGenericArgs(int, const char* const*, int&)
+{
+ return false;
+}
+
+bool
+client_stubCheckUnexpectedArgs()
+{
+ return false;
+}
+
+TEST(ClientArgsParsingTests, parseClientArgs_yScrollArg_setYScroll)
+{
+ NiceMock<MockArgParser> argParser;
+ ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(client_stubParseGenericArgs));
+ ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(client_stubCheckUnexpectedArgs));
+ ClientArgs clientArgs;
+ const int argc = 3;
+ const char* kYScrollCmd[argc] = { "stub", "--yscroll", "1" };
+
+ argParser.parseClientArgs(clientArgs, argc, kYScrollCmd);
+
+ EXPECT_EQ(1, clientArgs.m_yscroll);
+}
+
+TEST(ClientArgsParsingTests, parseClientArgs_addressArg_setBarrierAddress)
+{
+ NiceMock<MockArgParser> argParser;
+ ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(client_stubParseGenericArgs));
+ ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(client_stubCheckUnexpectedArgs));
+ ClientArgs clientArgs;
+ const int argc = 2;
+ const char* kAddressCmd[argc] = { "stub", "mock_address" };
+
+ bool result = argParser.parseClientArgs(clientArgs, argc, kAddressCmd);
+
+ EXPECT_EQ("mock_address", clientArgs.m_barrierAddress);
+ EXPECT_EQ(true, result);
+}
+
+TEST(ClientArgsParsingTests, parseClientArgs_noAddressArg_returnFalse)
+{
+ NiceMock<MockArgParser> argParser;
+ ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(client_stubParseGenericArgs));
+ ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(client_stubCheckUnexpectedArgs));
+ ClientArgs clientArgs;
+ const int argc = 1;
+ const char* kNoAddressCmd[argc] = { "stub" };
+
+ bool result = argParser.parseClientArgs(clientArgs, argc, kNoAddressCmd);
+
+ EXPECT_FALSE(result);
+}
+
+TEST(ClientArgsParsingTests, parseClientArgs_unrecognizedArg_returnFalse)
+{
+ NiceMock<MockArgParser> argParser;
+ ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(client_stubParseGenericArgs));
+ ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(client_stubCheckUnexpectedArgs));
+ ClientArgs clientArgs;
+ const int argc = 3;
+ const char* kUnrecognizedCmd[argc] = { "stub", "mock_arg", "mock_address"};
+
+ bool result = argParser.parseClientArgs(clientArgs, argc, kUnrecognizedCmd);
+
+ EXPECT_FALSE(result);
+}
diff --git a/src/test/unittests/barrier/ClipboardChunkTests.cpp b/src/test/unittests/barrier/ClipboardChunkTests.cpp
new file mode 100644
index 0000000..9dc4a5e
--- /dev/null
+++ b/src/test/unittests/barrier/ClipboardChunkTests.cpp
@@ -0,0 +1,128 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "barrier/ClipboardChunk.h"
+#include "barrier/protocol_types.h"
+
+#include "test/global/gtest.h"
+
+TEST(ClipboardChunkTests, start_formatStartChunk)
+{
+<<<<<<< HEAD
+ ClipboardID id = 0;
+ UInt32 sequence = 0;
+ String mockDataSize("10");
+ ClipboardChunk* chunk = ClipboardChunk::start(id, sequence, mockDataSize);
+
+ EXPECT_EQ(id, chunk->m_chunk[0]);
+ EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]);
+ EXPECT_EQ(kDataStart, chunk->m_chunk[5]);
+ EXPECT_EQ('1', chunk->m_chunk[6]);
+ EXPECT_EQ('0', chunk->m_chunk[7]);
+ EXPECT_EQ('\0', chunk->m_chunk[8]);
+=======
+ ClipboardID id = 0;
+ UInt32 sequence = 0;
+ String mockDataSize("10");
+ ClipboardChunk* chunk = ClipboardChunk::start(id, sequence, mockDataSize);
+ UInt32 temp_m_chunk;
+ memcpy(&temp_m_chunk, &(chunk->m_chunk[1]), 4);
+
+ EXPECT_EQ(id, chunk->m_chunk[0]);
+ EXPECT_EQ(sequence, temp_m_chunk);
+ EXPECT_EQ(kDataStart, chunk->m_chunk[5]);
+ EXPECT_EQ('1', chunk->m_chunk[6]);
+ EXPECT_EQ('0', chunk->m_chunk[7]);
+ EXPECT_EQ('\0', chunk->m_chunk[8]);
+>>>>>>> master
+
+ delete chunk;
+}
+
+TEST(ClipboardChunkTests, data_formatDataChunk)
+{
+<<<<<<< HEAD
+ ClipboardID id = 0;
+ UInt32 sequence = 1;
+ String mockData("mock data");
+ ClipboardChunk* chunk = ClipboardChunk::data(id, sequence, mockData);
+
+ EXPECT_EQ(id, chunk->m_chunk[0]);
+ EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]);
+ EXPECT_EQ(kDataChunk, chunk->m_chunk[5]);
+ EXPECT_EQ('m', chunk->m_chunk[6]);
+ EXPECT_EQ('o', chunk->m_chunk[7]);
+ EXPECT_EQ('c', chunk->m_chunk[8]);
+ EXPECT_EQ('k', chunk->m_chunk[9]);
+ EXPECT_EQ(' ', chunk->m_chunk[10]);
+ EXPECT_EQ('d', chunk->m_chunk[11]);
+ EXPECT_EQ('a', chunk->m_chunk[12]);
+ EXPECT_EQ('t', chunk->m_chunk[13]);
+ EXPECT_EQ('a', chunk->m_chunk[14]);
+ EXPECT_EQ('\0', chunk->m_chunk[15]);
+=======
+ ClipboardID id = 0;
+ UInt32 sequence = 1;
+ String mockData("mock data");
+ ClipboardChunk* chunk = ClipboardChunk::data(id, sequence, mockData);
+ UInt32 temp_m_chunk;
+ memcpy(&temp_m_chunk, &(chunk->m_chunk[1]), 4);
+
+ EXPECT_EQ(id, chunk->m_chunk[0]);
+ EXPECT_EQ(sequence, temp_m_chunk);
+ EXPECT_EQ(kDataChunk, chunk->m_chunk[5]);
+ EXPECT_EQ('m', chunk->m_chunk[6]);
+ EXPECT_EQ('o', chunk->m_chunk[7]);
+ EXPECT_EQ('c', chunk->m_chunk[8]);
+ EXPECT_EQ('k', chunk->m_chunk[9]);
+ EXPECT_EQ(' ', chunk->m_chunk[10]);
+ EXPECT_EQ('d', chunk->m_chunk[11]);
+ EXPECT_EQ('a', chunk->m_chunk[12]);
+ EXPECT_EQ('t', chunk->m_chunk[13]);
+ EXPECT_EQ('a', chunk->m_chunk[14]);
+ EXPECT_EQ('\0', chunk->m_chunk[15]);
+>>>>>>> master
+
+ delete chunk;
+}
+
+TEST(ClipboardChunkTests, end_formatDataChunk)
+{
+<<<<<<< HEAD
+ ClipboardID id = 1;
+ UInt32 sequence = 1;
+ ClipboardChunk* chunk = ClipboardChunk::end(id, sequence);
+
+ EXPECT_EQ(id, chunk->m_chunk[0]);
+ EXPECT_EQ(sequence, (UInt32)chunk->m_chunk[1]);
+ EXPECT_EQ(kDataEnd, chunk->m_chunk[5]);
+ EXPECT_EQ('\0', chunk->m_chunk[6]);
+=======
+ ClipboardID id = 1;
+ UInt32 sequence = 1;
+ ClipboardChunk* chunk = ClipboardChunk::end(id, sequence);
+ UInt32 temp_m_chunk;
+ memcpy(&temp_m_chunk, &(chunk->m_chunk[1]), 4);
+
+ EXPECT_EQ(id, chunk->m_chunk[0]);
+ EXPECT_EQ(sequence, temp_m_chunk);
+ EXPECT_EQ(kDataEnd, chunk->m_chunk[5]);
+ EXPECT_EQ('\0', chunk->m_chunk[6]);
+>>>>>>> master
+
+ delete chunk;
+}
diff --git a/src/test/unittests/barrier/ClipboardTests.cpp b/src/test/unittests/barrier/ClipboardTests.cpp
new file mode 100644
index 0000000..f710751
--- /dev/null
+++ b/src/test/unittests/barrier/ClipboardTests.cpp
@@ -0,0 +1,404 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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 "barrier/Clipboard.h"
+
+#include "test/global/gtest.h"
+
+TEST(ClipboardTests, empty_openCalled_returnsTrue)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+
+ bool actual = clipboard.empty();
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST(ClipboardTests, empty_singleFormat_hasReturnsFalse)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+ clipboard.add(Clipboard::kText, "barrier rocks!");
+
+ clipboard.empty();
+
+ bool actual = clipboard.has(Clipboard::kText);
+ EXPECT_FALSE(actual);
+}
+
+TEST(ClipboardTests, add_newValue_valueWasStored)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ String actual = clipboard.get(IClipboard::kText);
+ EXPECT_EQ("barrier rocks!", actual);
+}
+
+TEST(ClipboardTests, add_replaceValue_valueWasReplaced)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+ clipboard.add(IClipboard::kText, "maxivista sucks"); // haha, just kidding.
+
+ String actual = clipboard.get(IClipboard::kText);
+ EXPECT_EQ("maxivista sucks", actual);
+}
+
+TEST(ClipboardTests, open_timeIsZero_returnsTrue)
+{
+ Clipboard clipboard;
+
+ bool actual = clipboard.open(0);
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST(ClipboardTests, open_timeIsOne_returnsTrue)
+{
+ Clipboard clipboard;
+
+ bool actual = clipboard.open(1);
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST(ClipboardTests, close_isOpen_noErrors)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+
+ clipboard.close();
+
+ // can't assert anything
+}
+
+TEST(ClipboardTests, getTime_openWithNoEmpty_returnsZero)
+{
+ Clipboard clipboard;
+ clipboard.open(1);
+
+ Clipboard::Time actual = clipboard.getTime();
+
+ EXPECT_EQ(0, actual);
+}
+
+TEST(ClipboardTests, getTime_openAndEmpty_returnsOne)
+{
+ Clipboard clipboard;
+ clipboard.open(1);
+ clipboard.empty();
+
+ Clipboard::Time actual = clipboard.getTime();
+
+ EXPECT_EQ(1, actual);
+}
+
+TEST(ClipboardTests, has_withFormatAdded_returnsTrue)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ bool actual = clipboard.has(IClipboard::kText);
+
+ EXPECT_EQ(true, actual);
+}
+
+TEST(ClipboardTests, has_withNoFormats_returnsFalse)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+
+ bool actual = clipboard.has(IClipboard::kText);
+
+ EXPECT_FALSE(actual);
+}
+
+TEST(ClipboardTests, get_withNoFormats_returnsEmpty)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+
+ String actual = clipboard.get(IClipboard::kText);
+
+ EXPECT_EQ("", actual);
+}
+
+TEST(ClipboardTests, get_withFormatAdded_returnsExpected)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+
+ String actual = clipboard.get(IClipboard::kText);
+
+ EXPECT_EQ("barrier rocks!", actual);
+}
+
+TEST(ClipboardTests, marshall_addNotCalled_firstCharIsZero)
+{
+ Clipboard clipboard;
+
+ String actual = clipboard.marshall();
+
+ // seems to return "\0\0\0\0" but EXPECT_EQ can't assert this,
+ // so instead, just assert that first char is '\0'.
+ EXPECT_EQ(0, (int)actual[0]);
+}
+
+TEST(ClipboardTests, marshall_withTextAdded_typeCharIsText)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+ clipboard.close();
+
+ String actual = clipboard.marshall();
+
+ // string contains other data, but 8th char should be kText.
+ EXPECT_EQ(IClipboard::kText, (int)actual[7]);
+}
+
+TEST(ClipboardTests, marshall_withTextAdded_lastSizeCharIs14)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+ clipboard.add(IClipboard::kText, "barrier rocks!"); // 14 chars
+ clipboard.close();
+
+ String actual = clipboard.marshall();
+
+ EXPECT_EQ(14, (int)actual[11]);
+}
+
+// TODO: there's some integer -> char encoding going on here. i find it
+// hard to believe that the clipboard is the only thing doing this. maybe
+// we should refactor this stuff out of the clipboard.
+TEST(ClipboardTests, marshall_withTextSize285_sizeCharsValid)
+{
+ // 285 chars
+ String data;
+ data.append("Barrier is Free and Open Source Software that lets you ");
+ data.append("easily share your mouse and keyboard between multiple ");
+ data.append("computers, where each computer has it's own display. No ");
+ data.append("special hardware is required, all you need is a local area ");
+ data.append("network. Barrier is supported on Windows, Mac OS X and Linux.");
+
+ Clipboard clipboard;
+ clipboard.open(0);
+ clipboard.add(IClipboard::kText, data);
+ clipboard.close();
+
+ String actual = clipboard.marshall();
+
+ // 4 asserts here, but that's ok because we're really just asserting 1
+ // thing. the 32-bit size value is split into 4 chars. if the size is 285
+ // (29 more than the 8-bit max size), the last char "rolls over" to 29
+ // (this is caused by a bit-wise & on 0xff and 8-bit truncation). each
+ // char before the last stores a bit-shifted version of the number, each
+ // 1 more power than the last, which is done by bit-shifting [0] by 24,
+ // [1] by 16, [2] by 8 ([3] is not bit-shifted).
+ EXPECT_EQ(0, actual[8]); // 285 >> 24 = 285 / (256^3) = 0
+ EXPECT_EQ(0, actual[9]); // 285 >> 16 = 285 / (256^2) = 0
+ EXPECT_EQ(1, actual[10]); // 285 >> 8 = 285 / (256^1) = 1(.11328125)
+ EXPECT_EQ(29, actual[11]); // 285 - 256 = 29
+}
+
+TEST(ClipboardTests, marshall_withHtmlAdded_typeCharIsHtml)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+ clipboard.add(IClipboard::kHTML, "html sucks");
+ clipboard.close();
+
+ String actual = clipboard.marshall();
+
+ // string contains other data, but 8th char should be kHTML.
+ EXPECT_EQ(IClipboard::kHTML, (int)actual[7]);
+}
+
+TEST(ClipboardTests, marshall_withHtmlAndText_has2Formats)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+ clipboard.add(IClipboard::kText, "barrier rocks");
+ clipboard.add(IClipboard::kHTML, "html sucks");
+ clipboard.close();
+
+ String actual = clipboard.marshall();
+
+ // the number of formats is stored inside the first 4 chars.
+ // the writeUInt32 function right-aligns numbers in 4 chars,
+ // so if you right align 2, it will be "\0\0\0\2" in a string.
+ // we assert that the char at the 4th index is 2 (the number of
+ // formats that we've added).
+ EXPECT_EQ(2, (int)actual[3]);
+}
+
+TEST(ClipboardTests, marshall_withTextAdded_endsWithAdded)
+{
+ Clipboard clipboard;
+ clipboard.open(0);
+ clipboard.add(IClipboard::kText, "barrier rocks!");
+ clipboard.close();
+
+ String actual = clipboard.marshall();
+
+ // string contains other data, but should end in the string we added.
+ EXPECT_EQ("barrier rocks!", actual.substr(12));
+}
+
+TEST(ClipboardTests, unmarshall_emptyData_hasTextIsFalse)
+{
+ Clipboard clipboard;
+
+ String data;
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)0; // 0 formats added
+
+ clipboard.unmarshall(data, 0);
+
+ clipboard.open(0);
+ bool actual = clipboard.has(IClipboard::kText);
+ EXPECT_FALSE(actual);
+}
+
+TEST(ClipboardTests, unmarshall_withTextSize285_getTextIsValid)
+{
+ Clipboard clipboard;
+
+ // 285 chars
+ String text;
+ text.append("Barrier is Free and Open Source Software that lets you ");
+ text.append("easily share your mouse and keyboard between multiple ");
+ text.append("computers, where each computer has it's own display. No ");
+ text.append("special hardware is required, all you need is a local area ");
+ text.append("network. Barrier is supported on Windows, Mac OS X and Linux.");
+
+ String data;
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)1; // 1 format added
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)IClipboard::kText;
+ data += (char)0; // 285 >> 24 = 285 / (256^3) = 0
+ data += (char)0; // 285 >> 16 = 285 / (256^2) = 0
+ data += (char)1; // 285 >> 8 = 285 / (256^1) = 1(.11328125)
+ data += (char)29; // 285 - 256 = 29
+ data += text;
+
+ clipboard.unmarshall(data, 0);
+
+ clipboard.open(0);
+ String actual = clipboard.get(IClipboard::kText);
+ EXPECT_EQ(text, actual);
+}
+
+TEST(ClipboardTests, unmarshall_withTextAndHtml_getTextIsValid)
+{
+ Clipboard clipboard;
+ String data;
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)2; // 2 formats added
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)IClipboard::kText;
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)14;
+ data += "barrier rocks!";
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)IClipboard::kHTML;
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)10;
+ data += "html sucks";
+
+ clipboard.unmarshall(data, 0);
+
+ clipboard.open(0);
+ String actual = clipboard.get(IClipboard::kText);
+ EXPECT_EQ("barrier rocks!", actual);
+}
+
+TEST(ClipboardTests, unmarshall_withTextAndHtml_getHtmlIsValid)
+{
+ Clipboard clipboard;
+ String data;
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)2; // 2 formats added
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)IClipboard::kText;
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)14;
+ data += "barrier rocks!";
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)IClipboard::kHTML;
+ data += (char)0;
+ data += (char)0;
+ data += (char)0;
+ data += (char)10;
+ data += "html sucks";
+
+ clipboard.unmarshall(data, 0);
+
+ clipboard.open(0);
+ String actual = clipboard.get(IClipboard::kHTML);
+ EXPECT_EQ("html sucks", actual);
+}
+
+TEST(ClipboardTests, copy_withSingleText_clipboardsAreEqual)
+{
+ Clipboard clipboard1;
+ clipboard1.open(0);
+ clipboard1.add(Clipboard::kText, "barrier rocks!");
+ clipboard1.close();
+
+ Clipboard clipboard2;
+ Clipboard::copy(&clipboard2, &clipboard1);
+
+ clipboard2.open(0);
+ String actual = clipboard2.get(Clipboard::kText);
+ EXPECT_EQ("barrier rocks!", actual);
+}
diff --git a/src/test/unittests/barrier/DeprecatedArgsParsingTests.cpp b/src/test/unittests/barrier/DeprecatedArgsParsingTests.cpp
new file mode 100644
index 0000000..2856166
--- /dev/null
+++ b/src/test/unittests/barrier/DeprecatedArgsParsingTests.cpp
@@ -0,0 +1,50 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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 "barrier/ArgParser.h"
+
+#include "test/global/gtest.h"
+
+using namespace barrier;
+
+TEST(DeprecatedArgsParsingTests, parseDeprecatedArgs_cryptoPass_returnTrue)
+{
+ int i = 1;
+ const int argc = 3;
+ const char* kCryptoPassCmd[argc] = { "stub", "--crypto-pass", "mock_pass" };
+
+ ArgParser argParser(NULL);
+
+ bool result = argParser.parseDeprecatedArgs(argc, kCryptoPassCmd, i);
+
+ EXPECT_EQ(true, result);
+ EXPECT_EQ(2, i);
+}
+
+TEST(DeprecatedArgsParsingTests, parseDeprecatedArgs_cryptoPass_returnFalse)
+{
+ int i = 1;
+ const int argc = 3;
+ const char* kCryptoPassCmd[argc] = { "stub", "--mock-arg", "mock_value" };
+
+ ArgParser argParser(NULL);
+
+ bool result = argParser.parseDeprecatedArgs(argc, kCryptoPassCmd, i);
+
+ EXPECT_FALSE(result);
+ EXPECT_EQ(1, i);
+}
diff --git a/src/test/unittests/barrier/GenericArgsParsingTests.cpp b/src/test/unittests/barrier/GenericArgsParsingTests.cpp
new file mode 100644
index 0000000..f43070b
--- /dev/null
+++ b/src/test/unittests/barrier/GenericArgsParsingTests.cpp
@@ -0,0 +1,315 @@
+/*
+ * barrier -- 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 "barrier/ArgParser.h"
+#include "barrier/ArgsBase.h"
+#include "test/mock/barrier/MockApp.h"
+
+#include "test/global/gtest.h"
+
+using namespace barrier;
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+
+bool g_helpShowed = false;
+bool g_versionShowed = false;
+
+void
+showMockHelp()
+{
+ g_helpShowed = true;
+}
+
+void
+showMockVersion()
+{
+ g_versionShowed = true;
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_logLevelCmd_setLogLevel)
+{
+ int i = 1;
+ const int argc = 3;
+ const char* kLogLevelCmd[argc] = { "stub", "--debug", "DEBUG" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kLogLevelCmd, i);
+
+ String logFilter(argsBase.m_logFilter);
+
+ EXPECT_EQ("DEBUG", logFilter);
+ EXPECT_EQ(2, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_logFileCmd_saveLogFilename)
+{
+ int i = 1;
+ const int argc = 3;
+ const char* kLogFileCmd[argc] = { "stub", "--log", "mock_filename" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kLogFileCmd, i);
+
+ String logFile(argsBase.m_logFile);
+
+ EXPECT_EQ("mock_filename", logFile);
+ EXPECT_EQ(2, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_logFileCmdWithSpace_saveLogFilename)
+{
+ int i = 1;
+ const int argc = 3;
+ const char* kLogFileCmdWithSpace[argc] = { "stub", "--log", "mo ck_filename" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kLogFileCmdWithSpace, i);
+
+ String logFile(argsBase.m_logFile);
+
+ EXPECT_EQ("mo ck_filename", logFile);
+ EXPECT_EQ(2, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_noDeamonCmd_daemonFalse)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* kNoDeamonCmd[argc] = { "stub", "-f" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kNoDeamonCmd, i);
+
+ EXPECT_FALSE(argsBase.m_daemon);
+ EXPECT_EQ(1, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_deamonCmd_daemonTrue)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* kDeamonCmd[argc] = { "stub", "--daemon" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kDeamonCmd, i);
+
+ EXPECT_EQ(true, argsBase.m_daemon);
+ EXPECT_EQ(1, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_nameCmd_saveName)
+{
+ int i = 1;
+ const int argc = 3;
+ const char* kNameCmd[argc] = { "stub", "--name", "mock" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kNameCmd, i);
+
+ EXPECT_EQ("mock", argsBase.m_name);
+ EXPECT_EQ(2, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_noRestartCmd_restartFalse)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* kNoRestartCmd[argc] = { "stub", "--no-restart" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kNoRestartCmd, i);
+
+ EXPECT_FALSE(argsBase.m_restartable);
+ EXPECT_EQ(1, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_restartCmd_restartTrue)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* kRestartCmd[argc] = { "stub", "--restart" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kRestartCmd, i);
+
+ EXPECT_EQ(true, argsBase.m_restartable);
+ EXPECT_EQ(1, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_backendCmd_backendTrue)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* kBackendCmd[argc] = { "stub", "-z" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kBackendCmd, i);
+
+ EXPECT_EQ(true, argsBase.m_backend);
+ EXPECT_EQ(1, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_noHookCmd_noHookTrue)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* kNoHookCmd[argc] = { "stub", "--no-hooks" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kNoHookCmd, i);
+
+ EXPECT_EQ(true, argsBase.m_noHooks);
+ EXPECT_EQ(1, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_helpCmd_showHelp)
+{
+ g_helpShowed = false;
+ int i = 1;
+ const int argc = 2;
+ const char* kHelpCmd[argc] = { "stub", "--help" };
+
+ NiceMock<MockApp> app;
+ ArgParser argParser(&app);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+ ON_CALL(app, help()).WillByDefault(Invoke(showMockHelp));
+
+ argParser.parseGenericArgs(argc, kHelpCmd, i);
+
+ EXPECT_EQ(true, g_helpShowed);
+ EXPECT_EQ(1, i);
+}
+
+
+TEST(GenericArgsParsingTests, parseGenericArgs_versionCmd_showVersion)
+{
+ g_versionShowed = false;
+ int i = 1;
+ const int argc = 2;
+ const char* kVersionCmd[argc] = { "stub", "--version" };
+
+ NiceMock<MockApp> app;
+ ArgParser argParser(&app);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+ ON_CALL(app, version()).WillByDefault(Invoke(showMockVersion));
+
+ argParser.parseGenericArgs(argc, kVersionCmd, i);
+
+ EXPECT_EQ(true, g_versionShowed);
+ EXPECT_EQ(1, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_noTrayCmd_disableTrayTrue)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* kNoTrayCmd[argc] = { "stub", "--no-tray" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kNoTrayCmd, i);
+
+ EXPECT_EQ(true, argsBase.m_disableTray);
+ EXPECT_EQ(1, i);
+}
+
+TEST(GenericArgsParsingTests, parseGenericArgs_ipcCmd_enableIpcTrue)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* kIpcCmd[argc] = { "stub", "--ipc" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kIpcCmd, i);
+
+ EXPECT_EQ(true, argsBase.m_enableIpc);
+ EXPECT_EQ(1, i);
+}
+
+#ifndef WINAPI_XWINDOWS
+TEST(GenericArgsParsingTests, parseGenericArgs_dragDropCmdOnNonLinux_enableDragDropTrue)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* kDragDropCmd[argc] = { "stub", "--enable-drag-drop" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kDragDropCmd, i);
+
+ EXPECT_EQ(true, argsBase.m_enableDragDrop);
+ EXPECT_EQ(1, i);
+}
+#endif
+
+#ifdef WINAPI_XWINDOWS
+TEST(GenericArgsParsingTests, parseGenericArgs_dragDropCmdOnLinux_enableDragDropFalse)
+{
+ int i = 1;
+ const int argc = 2;
+ const char* kDragDropCmd[argc] = { "stub", "--enable-drag-drop" };
+
+ ArgParser argParser(NULL);
+ ArgsBase argsBase;
+ argParser.setArgsBase(argsBase);
+
+ argParser.parseGenericArgs(argc, kDragDropCmd, i);
+
+ EXPECT_FALSE(argsBase.m_enableDragDrop);
+ EXPECT_EQ(1, i);
+}
+#endif
diff --git a/src/test/unittests/barrier/KeyMapTests.cpp b/src/test/unittests/barrier/KeyMapTests.cpp
new file mode 100644
index 0000000..5980633
--- /dev/null
+++ b/src/test/unittests/barrier/KeyMapTests.cpp
@@ -0,0 +1,216 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2016 Symless
+ *
+ * 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 "barrier/KeyMap.h"
+
+#include "test/global/gtest.h"
+#include "test/global/gmock.h"
+
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SaveArg;
+
+namespace barrier {
+
+TEST(KeyMapTests, findBestKey_requiredDown_matchExactFirstItem)
+{
+ KeyMap keyMap;
+ KeyMap::KeyEntryList entryList;
+ KeyMap::KeyItemList itemList;
+ KeyMap::KeyItem item;
+ item.m_required = KeyModifierShift;
+ item.m_sensitive = KeyModifierShift;
+ KeyModifierMask currentState = KeyModifierShift;
+ KeyModifierMask desiredState = KeyModifierShift;
+ itemList.push_back(item);
+ entryList.push_back(itemList);
+
+ EXPECT_EQ(0, keyMap.findBestKey(entryList, currentState, desiredState));
+}
+
+TEST(KeyMapTests, findBestKey_requiredAndExtraSensitiveDown_matchExactFirstItem)
+{
+ KeyMap keyMap;
+ KeyMap::KeyEntryList entryList;
+ KeyMap::KeyItemList itemList;
+ KeyMap::KeyItem item;
+ item.m_required = KeyModifierShift;
+ item.m_sensitive = KeyModifierShift | KeyModifierAlt;
+ KeyModifierMask currentState = KeyModifierShift;
+ KeyModifierMask desiredState = KeyModifierShift;
+ itemList.push_back(item);
+ entryList.push_back(itemList);
+
+ EXPECT_EQ(0, keyMap.findBestKey(entryList, currentState, desiredState));
+}
+
+TEST(KeyMapTests, findBestKey_requiredAndExtraSensitiveDown_matchExactSecondItem)
+{
+ KeyMap keyMap;
+ KeyMap::KeyEntryList entryList;
+ KeyMap::KeyItemList itemList1;
+ KeyMap::KeyItem item1;
+ item1.m_required = KeyModifierAlt;
+ item1.m_sensitive = KeyModifierShift | KeyModifierAlt;
+ KeyMap::KeyItemList itemList2;
+ KeyMap::KeyItem item2;
+ item2.m_required = KeyModifierShift;
+ item2.m_sensitive = KeyModifierShift | KeyModifierAlt;
+ KeyModifierMask currentState = KeyModifierShift;
+ KeyModifierMask desiredState = KeyModifierShift;
+ itemList1.push_back(item1);
+ itemList2.push_back(item2);
+ entryList.push_back(itemList1);
+ entryList.push_back(itemList2);
+
+ EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState));
+}
+
+TEST(KeyMapTests, findBestKey_extraSensitiveDown_matchExactSecondItem)
+{
+ KeyMap keyMap;
+ KeyMap::KeyEntryList entryList;
+ KeyMap::KeyItemList itemList1;
+ KeyMap::KeyItem item1;
+ item1.m_required = 0;
+ item1.m_sensitive = KeyModifierAlt;
+ KeyMap::KeyItemList itemList2;
+ KeyMap::KeyItem item2;
+ item2.m_required = 0;
+ item2.m_sensitive = KeyModifierShift;
+ KeyModifierMask currentState = KeyModifierAlt;
+ KeyModifierMask desiredState = KeyModifierAlt;
+ itemList1.push_back(item1);
+ itemList2.push_back(item2);
+ entryList.push_back(itemList1);
+ entryList.push_back(itemList2);
+
+ EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState));
+}
+
+TEST(KeyMapTests, findBestKey_noRequiredDown_matchOneRequiredChangeItem)
+{
+ KeyMap keyMap;
+ KeyMap::KeyEntryList entryList;
+ KeyMap::KeyItemList itemList1;
+ KeyMap::KeyItem item1;
+ item1.m_required = KeyModifierShift | KeyModifierAlt;
+ item1.m_sensitive = KeyModifierShift | KeyModifierAlt;
+ KeyMap::KeyItemList itemList2;
+ KeyMap::KeyItem item2;
+ item2.m_required = KeyModifierShift;
+ item2.m_sensitive = KeyModifierShift | KeyModifierAlt;
+ KeyModifierMask currentState = 0;
+ KeyModifierMask desiredState = 0;
+ itemList1.push_back(item1);
+ itemList2.push_back(item2);
+ entryList.push_back(itemList1);
+ entryList.push_back(itemList2);
+
+ EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState));
+}
+
+TEST(KeyMapTests, findBestKey_onlyOneRequiredDown_matchTwoRequiredChangesItem)
+{
+ KeyMap keyMap;
+ KeyMap::KeyEntryList entryList;
+ KeyMap::KeyItemList itemList1;
+ KeyMap::KeyItem item1;
+ item1.m_required = KeyModifierShift | KeyModifierAlt | KeyModifierControl;
+ item1.m_sensitive = KeyModifierShift | KeyModifierAlt | KeyModifierControl;
+ KeyMap::KeyItemList itemList2;
+ KeyMap::KeyItem item2;
+ item2.m_required = KeyModifierShift| KeyModifierAlt;
+ item2.m_sensitive = KeyModifierShift | KeyModifierAlt | KeyModifierControl;
+ KeyModifierMask currentState = 0;
+ KeyModifierMask desiredState = 0;
+ itemList1.push_back(item1);
+ itemList2.push_back(item2);
+ entryList.push_back(itemList1);
+ entryList.push_back(itemList2);
+
+ EXPECT_EQ(1, keyMap.findBestKey(entryList, currentState, desiredState));
+}
+
+TEST(KeyMapTests, findBestKey_noRequiredDown_cannotMatch)
+{
+ KeyMap keyMap;
+ KeyMap::KeyEntryList entryList;
+ KeyMap::KeyItemList itemList;
+ KeyMap::KeyItem item;
+ item.m_required = 0xffffffff;
+ item.m_sensitive = 0xffffffff;
+ KeyModifierMask currentState = 0;
+ KeyModifierMask desiredState = 0;
+ itemList.push_back(item);
+ entryList.push_back(itemList);
+
+ EXPECT_EQ(-1, keyMap.findBestKey(entryList, currentState, desiredState));
+}
+
+TEST(KeyMapTests, isCommand_shiftMask_returnFalse)
+{
+ KeyMap keyMap;
+ KeyModifierMask mask= KeyModifierShift;
+
+ EXPECT_FALSE(keyMap.isCommand(mask));
+}
+
+TEST(KeyMapTests, isCommand_controlMask_returnTrue)
+{
+ KeyMap keyMap;
+ KeyModifierMask mask= KeyModifierControl;
+
+ EXPECT_EQ(true, keyMap.isCommand(mask));
+}
+
+TEST(KeyMapTests, isCommand_alternateMask_returnTrue)
+{
+ KeyMap keyMap;
+ KeyModifierMask mask= KeyModifierAlt;
+
+ EXPECT_EQ(true, keyMap.isCommand(mask));
+}
+
+TEST(KeyMapTests, isCommand_alternateGraphicMask_returnTrue)
+{
+ KeyMap keyMap;
+ KeyModifierMask mask= KeyModifierAltGr;
+
+ EXPECT_EQ(true, keyMap.isCommand(mask));
+}
+
+TEST(KeyMapTests, isCommand_metaMask_returnTrue)
+{
+ KeyMap keyMap;
+ KeyModifierMask mask= KeyModifierMeta;
+
+ EXPECT_EQ(true, keyMap.isCommand(mask));
+}
+
+TEST(KeyMapTests, isCommand_superMask_returnTrue)
+{
+ KeyMap keyMap;
+ KeyModifierMask mask= KeyModifierSuper;
+
+ EXPECT_EQ(true, keyMap.isCommand(mask));
+}
+
+}
diff --git a/src/test/unittests/barrier/KeyStateTests.cpp b/src/test/unittests/barrier/KeyStateTests.cpp
new file mode 100644
index 0000000..d4154d8
--- /dev/null
+++ b/src/test/unittests/barrier/KeyStateTests.cpp
@@ -0,0 +1,488 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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/mock/barrier/MockKeyState.h"
+#include "test/mock/barrier/MockEventQueue.h"
+#include "test/mock/barrier/MockKeyMap.h"
+
+#include "test/global/gtest.h"
+#include "test/global/gmock.h"
+
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::SaveArg;
+
+void
+stubPollPressedKeys(IKeyState::KeyButtonSet& pressedKeys);
+
+void
+assertMaskIsOne(ForeachKeyCallback cb, void* userData);
+
+const barrier::KeyMap::KeyItem*
+stubMapKey(
+ barrier::KeyMap::Keystrokes& keys, KeyID id, SInt32 group,
+ barrier::KeyMap::ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat);
+
+barrier::KeyMap::Keystroke s_stubKeystroke(1, false, false);
+barrier::KeyMap::KeyItem s_stubKeyItem;
+
+TEST(CKeyStateTests, onKey_aKeyDown_keyStateOne)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ keyState.onKey(1, true, KeyModifierAlt);
+
+ EXPECT_EQ(1, keyState.getKeyState(1));
+}
+
+TEST(KeyStateTests, onKey_aKeyUp_keyStateZero)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ keyState.onKey(1, false, KeyModifierAlt);
+
+ EXPECT_EQ(0, keyState.getKeyState(1));
+}
+
+TEST(KeyStateTests, onKey_invalidKey_keyStateZero)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ keyState.onKey(0, true, KeyModifierAlt);
+
+ EXPECT_EQ(0, keyState.getKeyState(0));
+}
+
+TEST(KeyStateTests, sendKeyEvent_halfDuplexAndRepeat_addEventNotCalled)
+{
+ NiceMock<MockKeyMap> keyMap;
+ NiceMock<MockEventQueue> eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ ON_CALL(keyMap, isHalfDuplex(_, _)).WillByDefault(Return(true));
+
+ EXPECT_CALL(eventQueue, addEvent(_)).Times(0);
+
+ keyState.sendKeyEvent(NULL, false, true, kKeyCapsLock, 0, 0, 0);
+}
+
+TEST(KeyStateTests, sendKeyEvent_halfDuplex_addEventCalledTwice)
+{
+ NiceMock<MockKeyMap> keyMap;
+ NiceMock<MockEventQueue> eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+ IKeyStateEvents keyStateEvents;
+ keyStateEvents.setEvents(&eventQueue);
+
+ ON_CALL(keyMap, isHalfDuplex(_, _)).WillByDefault(Return(true));
+ ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents));
+
+ EXPECT_CALL(eventQueue, addEvent(_)).Times(2);
+
+ keyState.sendKeyEvent(NULL, false, false, kKeyCapsLock, 0, 0, 0);
+}
+
+TEST(KeyStateTests, sendKeyEvent_keyRepeat_addEventCalledOnce)
+{
+ NiceMock<MockKeyMap> keyMap;
+ NiceMock<MockEventQueue> eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+ IKeyStateEvents keyStateEvents;
+ keyStateEvents.setEvents(&eventQueue);
+
+ ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents));
+
+ EXPECT_CALL(eventQueue, addEvent(_)).Times(1);
+
+ keyState.sendKeyEvent(NULL, false, true, 1, 0, 0, 0);
+}
+
+TEST(KeyStateTests, sendKeyEvent_keyDown_addEventCalledOnce)
+{
+ NiceMock<MockKeyMap> keyMap;
+ NiceMock<MockEventQueue> eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+ IKeyStateEvents keyStateEvents;
+ keyStateEvents.setEvents(&eventQueue);
+
+ ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents));
+
+ EXPECT_CALL(eventQueue, addEvent(_)).Times(1);
+
+ keyState.sendKeyEvent(NULL, true, false, 1, 0, 0, 0);
+}
+
+TEST(KeyStateTests, sendKeyEvent_keyUp_addEventCalledOnce)
+{
+ NiceMock<MockKeyMap> keyMap;
+ NiceMock<MockEventQueue> eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+ IKeyStateEvents keyStateEvents;
+ keyStateEvents.setEvents(&eventQueue);
+
+ ON_CALL(eventQueue, forIKeyState()).WillByDefault(ReturnRef(keyStateEvents));
+
+ EXPECT_CALL(eventQueue, addEvent(_)).Times(1);
+
+ keyState.sendKeyEvent(NULL, false, false, 1, 0, 0, 0);
+}
+
+TEST(KeyStateTests, updateKeyMap_mockKeyMap_keyMapGotMock)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ // key map member gets a new key map via swap()
+ EXPECT_CALL(keyMap, swap(_));
+
+ keyState.updateKeyMap();
+}
+
+TEST(KeyStateTests, updateKeyState_pollInsertsSingleKey_keyIsDown)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+ ON_CALL(keyState, pollPressedKeys(_)).WillByDefault(Invoke(stubPollPressedKeys));
+
+ keyState.updateKeyState();
+
+ bool actual = keyState.isKeyDown(1);
+ ASSERT_TRUE(actual);
+}
+
+TEST(KeyStateTests, updateKeyState_pollDoesNothing_keyNotSet)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ keyState.updateKeyState();
+
+ bool actual = keyState.isKeyDown(1);
+ ASSERT_FALSE(actual);
+}
+
+TEST(KeyStateTests, updateKeyState_activeModifiers_maskSet)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+ ON_CALL(keyState, pollActiveModifiers()).WillByDefault(Return(KeyModifierAlt));
+
+ keyState.updateKeyState();
+
+ KeyModifierMask actual = keyState.getActiveModifiers();
+ ASSERT_EQ(KeyModifierAlt, actual);
+}
+
+TEST(KeyStateTests, updateKeyState_activeModifiers_maskNotSet)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ keyState.updateKeyState();
+
+ KeyModifierMask actual = keyState.getActiveModifiers();
+ ASSERT_EQ(0, actual);
+}
+
+TEST(KeyStateTests, updateKeyState_activeModifiers_keyMapGotModifers)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+ ON_CALL(keyState, pollActiveModifiers()).WillByDefault(Return(1));
+ ON_CALL(keyMap, foreachKey(_, _)).WillByDefault(Invoke(assertMaskIsOne));
+
+ // key map gets new modifiers via foreachKey()
+ EXPECT_CALL(keyMap, foreachKey(_, _));
+
+ keyState.updateKeyState();
+}
+
+TEST(KeyStateTests, setHalfDuplexMask_capsLock_halfDuplexCapsLockAdded)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ EXPECT_CALL(keyMap, addHalfDuplexModifier(kKeyCapsLock));
+
+ keyState.setHalfDuplexMask(KeyModifierCapsLock);
+}
+
+TEST(KeyStateTests, setHalfDuplexMask_numLock_halfDuplexNumLockAdded)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ EXPECT_CALL(keyMap, addHalfDuplexModifier(kKeyNumLock));
+
+ keyState.setHalfDuplexMask(KeyModifierNumLock);
+}
+
+TEST(KeyStateTests, setHalfDuplexMask_scrollLock_halfDuplexScollLockAdded)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ EXPECT_CALL(keyMap, addHalfDuplexModifier(kKeyScrollLock));
+
+ keyState.setHalfDuplexMask(KeyModifierScrollLock);
+}
+
+TEST(KeyStateTests, fakeKeyDown_serverKeyAlreadyDown_fakeKeyCalledTwice)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+ s_stubKeyItem.m_client = 0;
+ s_stubKeyItem.m_button = 1;
+ 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);
+}
+
+TEST(KeyStateTests, fakeKeyDown_isIgnoredKey_fakeKeyNotCalled)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ EXPECT_CALL(keyState, fakeKey(_)).Times(0);
+
+ keyState.fakeKeyDown(kKeyCapsLock, 0, 0);
+}
+
+TEST(KeyStateTests, fakeKeyDown_mapReturnsKeystrokes_fakeKeyCalled)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+ s_stubKeyItem.m_button = 0;
+ s_stubKeyItem.m_client = 0;
+ ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
+
+ EXPECT_CALL(keyState, fakeKey(_)).Times(1);
+
+ keyState.fakeKeyDown(1, 0, 0);
+}
+
+TEST(KeyStateTests, fakeKeyRepeat_invalidKey_returnsFalse)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ bool actual = keyState.fakeKeyRepeat(0, 0, 0, 0);
+
+ ASSERT_FALSE(actual);
+}
+
+TEST(KeyStateTests, fakeKeyRepeat_nullKey_returnsFalse)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ // set the key to down (we need to make mapKey return a valid key to do this).
+ barrier::KeyMap::KeyItem keyItem;
+ keyItem.m_client = 0;
+ keyItem.m_button = 1;
+ ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
+ keyState.fakeKeyDown(1, 0, 0);
+
+ // change mapKey to return NULL so that fakeKeyRepeat exits early.
+ barrier::KeyMap::KeyItem* nullKeyItem = NULL;
+ ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(nullKeyItem));
+
+ bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0);
+
+ ASSERT_FALSE(actual);
+}
+
+TEST(KeyStateTests, fakeKeyRepeat_invalidButton_returnsFalse)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ // set the key to down (we need to make mapKey return a valid key to do this).
+ barrier::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);
+
+ // change button to 0 so that fakeKeyRepeat will return early.
+ keyItem.m_button = 0;
+ ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Return(&keyItem));
+
+ bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0);
+
+ ASSERT_FALSE(actual);
+}
+
+TEST(KeyStateTests, fakeKeyRepeat_validKey_returnsTrue)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+ s_stubKeyItem.m_client = 0;
+ s_stubKeystroke.m_type = barrier::KeyMap::Keystroke::kButton;
+ s_stubKeystroke.m_data.m_button.m_button = 2;
+
+ // 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);
+
+ // change the button to 2
+ s_stubKeyItem.m_button = 2;
+ ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
+
+ bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0);
+
+ ASSERT_TRUE(actual);
+}
+
+TEST(KeyStateTests, fakeKeyUp_buttonNotDown_returnsFalse)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ bool actual = keyState.fakeKeyUp(0);
+
+ ASSERT_FALSE(actual);
+}
+
+TEST(KeyStateTests, fakeKeyUp_buttonAlreadyDown_returnsTrue)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ // press alt down so we get full coverage.
+ ON_CALL(keyState, pollActiveModifiers()).WillByDefault(Return(KeyModifierAlt));
+ keyState.updateKeyState();
+
+ // press button 1 down.
+ s_stubKeyItem.m_button = 1;
+ ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
+ keyState.fakeKeyDown(1, 0, 1);
+
+ // this takes the button id, which is the 3rd arg of fakeKeyDown
+ bool actual = keyState.fakeKeyUp(1);
+
+ ASSERT_TRUE(actual);
+}
+
+TEST(KeyStateTests, fakeAllKeysUp_keysWereDown_keysAreUp)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ // press button 1 down.
+ s_stubKeyItem.m_button = 1;
+ ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
+ keyState.fakeKeyDown(1, 0, 1);
+
+ // method under test
+ keyState.fakeAllKeysUp();
+
+ bool actual = keyState.isKeyDown(1);
+ ASSERT_FALSE(actual);
+}
+
+TEST(KeyStateTests, isKeyDown_keyDown_returnsTrue)
+{
+ NiceMock<MockKeyMap> keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ // press button 1 down.
+ s_stubKeyItem.m_button = 1;
+ ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey));
+ keyState.fakeKeyDown(1, 0, 1);
+
+ // method under test
+ bool actual = keyState.isKeyDown(1);
+
+ ASSERT_TRUE(actual);
+}
+
+TEST(KeyStateTests, isKeyDown_noKeysDown_returnsFalse)
+{
+ MockKeyMap keyMap;
+ MockEventQueue eventQueue;
+ KeyStateImpl keyState(eventQueue, keyMap);
+
+ // method under test
+ bool actual = keyState.isKeyDown(1);
+
+ ASSERT_FALSE(actual);
+}
+
+void
+stubPollPressedKeys(IKeyState::KeyButtonSet& pressedKeys)
+{
+ pressedKeys.insert(1);
+}
+
+void
+assertMaskIsOne(ForeachKeyCallback cb, void* userData)
+{
+ ASSERT_EQ(1, ((KeyState::AddActiveModifierContext*)userData)->m_mask);
+}
+
+const barrier::KeyMap::KeyItem*
+stubMapKey(
+ barrier::KeyMap::Keystrokes& keys, KeyID id, SInt32 group,
+ barrier::KeyMap::ModifierToKeys& activeModifiers,
+ KeyModifierMask& currentState,
+ KeyModifierMask desiredMask,
+ bool isAutoRepeat)
+{
+ keys.push_back(s_stubKeystroke);
+ return &s_stubKeyItem;
+}
diff --git a/src/test/unittests/barrier/ServerArgsParsingTests.cpp b/src/test/unittests/barrier/ServerArgsParsingTests.cpp
new file mode 100644
index 0000000..ed5ed85
--- /dev/null
+++ b/src/test/unittests/barrier/ServerArgsParsingTests.cpp
@@ -0,0 +1,66 @@
+/*
+ * barrier -- 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 "barrier/ArgParser.h"
+#include "barrier/ServerArgs.h"
+#include "test/mock/barrier/MockArgParser.h"
+
+#include "test/global/gtest.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+
+bool
+server_stubParseGenericArgs(int, const char* const*, int&)
+{
+ return false;
+}
+
+bool
+server_stubCheckUnexpectedArgs()
+{
+ return false;
+}
+
+TEST(ServerArgsParsingTests, parseServerArgs_addressArg_setBarrierAddress)
+{
+ NiceMock<MockArgParser> argParser;
+ ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(server_stubParseGenericArgs));
+ ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(server_stubCheckUnexpectedArgs));
+ ServerArgs serverArgs;
+ const int argc = 3;
+ const char* kAddressCmd[argc] = { "stub", "--address", "mock_address" };
+
+ argParser.parseServerArgs(serverArgs, argc, kAddressCmd);
+
+ EXPECT_EQ("mock_address", serverArgs.m_barrierAddress);
+}
+
+TEST(ServerArgsParsingTests, parseServerArgs_configArg_setConfigFile)
+{
+ NiceMock<MockArgParser> argParser;
+ ON_CALL(argParser, parseGenericArgs(_, _, _)).WillByDefault(Invoke(server_stubParseGenericArgs));
+ ON_CALL(argParser, checkUnexpectedArgs()).WillByDefault(Invoke(server_stubCheckUnexpectedArgs));
+ ServerArgs serverArgs;
+ const int argc = 3;
+ const char* kConfigCmd[argc] = { "stub", "--config", "mock_configFile" };
+
+ argParser.parseServerArgs(serverArgs, argc, kConfigCmd);
+
+ EXPECT_EQ("mock_configFile", serverArgs.m_configFile);
+}
diff --git a/src/test/unittests/base/StringTests.cpp b/src/test/unittests/base/StringTests.cpp
new file mode 100644
index 0000000..39ad6e8
--- /dev/null
+++ b/src/test/unittests/base/StringTests.cpp
@@ -0,0 +1,177 @@
+/*
+ * barrier -- 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 "base/String.h"
+
+#include "test/global/gtest.h"
+
+using namespace barrier;
+
+TEST(StringTests, format_formatWithArguments_formatedString)
+{
+ const char* format = "%%%{1}=%{2}";
+ const char* arg1 = "answer";
+ const char* arg2 = "42";
+
+ String result = string::format(format, arg1, arg2);
+
+ EXPECT_EQ("%answer=42", result);
+}
+
+TEST(StringTests, findReplaceAll_inputString_replacedString)
+{
+ String subject = "foobar";
+ String find = "bar";
+ String replace = "baz";
+
+ string::findReplaceAll(subject, find, replace);
+
+ EXPECT_EQ("foobaz", subject);
+}
+
+TEST(StringTests, sprintf_formatWithArgument_formatedString)
+{
+ const char* format = "%s=%d";
+ const char* arg1 = "answer";
+ int arg2 = 42;
+
+ String result = string::sprintf(format, arg1, arg2);
+
+ EXPECT_EQ("answer=42", result);
+}
+
+TEST(StringTests, toHex_plaintext_hexString)
+{
+ String subject = "foobar";
+ int width = 2;
+
+ string::toHex(subject, width);
+
+ EXPECT_EQ("666f6f626172", subject);
+}
+
+TEST(StringTests, uppercase_lowercaseInput_uppercaseOutput)
+{
+ String subject = "12foo3BaR";
+
+ string::uppercase(subject);
+
+ EXPECT_EQ("12FOO3BAR", subject);
+}
+
+TEST(StringTests, removeChar_inputString_removeAllSpecifiedCharactors)
+{
+ String subject = "foobar";
+ const char c = 'o';
+
+ string::removeChar(subject, c);
+
+ EXPECT_EQ("fbar", subject);
+}
+
+TEST(StringTests, intToString_inputInt_outputString)
+{
+ size_t value = 123;
+
+ String number = string::sizeTypeToString(value);
+
+ EXPECT_EQ("123", number);
+}
+
+TEST(StringTests, stringToUint_inputString_outputInt)
+{
+ String number = "123";
+
+ size_t value = string::stringToSizeType(number);
+
+ EXPECT_EQ(123, value);
+}
+
+TEST(StringTests, splitString_twoSeparator_returnThreeParts)
+{
+ String string = "stub1:stub2:stub3";
+
+ std::vector<String> results = string::splitString(string, ':');
+
+ EXPECT_EQ(3, results.size());
+ EXPECT_EQ("stub1", results[0]);
+ EXPECT_EQ("stub2", results[1]);
+ EXPECT_EQ("stub3", results[2]);
+}
+
+TEST(StringTests, splitString_oneSeparator_returnTwoParts)
+{
+ String string = "stub1:stub2";
+
+ std::vector<String> results = string::splitString(string, ':');
+
+ EXPECT_EQ(2, results.size());
+ EXPECT_EQ("stub1", results[0]);
+ EXPECT_EQ("stub2", results[1]);
+}
+
+TEST(StringTests, splitString_noSeparator_returnOriginalString)
+{
+ String string = "stub1";
+
+ std::vector<String> results = string::splitString(string, ':');
+
+ EXPECT_EQ(1, results.size());
+ EXPECT_EQ("stub1", results[0]);
+}
+
+TEST(StringTests, splitString_emptyString_returnEmptyVector)
+{
+ String string;
+
+ std::vector<String> results = string::splitString(string, ':');
+
+ EXPECT_EQ(0, results.size());
+}
+
+TEST(StringTests, splitString_tailSeparator_returnTwoParts)
+{
+ String string = "stub1:stub2:";
+
+ std::vector<String> results = string::splitString(string, ':');
+
+ EXPECT_EQ(2, results.size());
+ EXPECT_EQ("stub1", results[0]);
+ EXPECT_EQ("stub2", results[1]);
+}
+
+TEST(StringTests, splitString_headSeparator_returnTwoParts)
+{
+ String string = ":stub1:stub2";
+
+ std::vector<String> results = string::splitString(string, ':');
+
+ EXPECT_EQ(2, results.size());
+ EXPECT_EQ("stub1", results[0]);
+ EXPECT_EQ("stub2", results[1]);
+}
+
+TEST(StringTests, splitString_headAndTailSeparators_returnTwoParts)
+{
+ String string = ":stub1:stub2:";
+
+ std::vector<String> results = string::splitString(string, ':');
+
+ EXPECT_EQ(2, results.size());
+ EXPECT_EQ("stub1", results[0]);
+ EXPECT_EQ("stub2", results[1]);
+}
diff --git a/src/test/unittests/ipc/IpcLogOutputterTests.cpp b/src/test/unittests/ipc/IpcLogOutputterTests.cpp
new file mode 100644
index 0000000..bbfed9c
--- /dev/null
+++ b/src/test/unittests/ipc/IpcLogOutputterTests.cpp
@@ -0,0 +1,165 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2015-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/>.
+ */
+
+#define TEST_ENV
+
+#include "test/mock/ipc/MockIpcServer.h"
+
+#include "mt/Thread.h"
+#include "ipc/IpcLogOutputter.h"
+#include "base/String.h"
+#include "common/common.h"
+
+#include "test/global/gmock.h"
+#include "test/global/gtest.h"
+
+// HACK: ipc logging only used on windows anyway
+#if WINAPI_MSWINDOWS
+
+using ::testing::_;
+using ::testing::Return;
+using ::testing::Matcher;
+using ::testing::MatcherCast;
+using ::testing::Property;
+using ::testing::StrEq;
+using ::testing::AtLeast;
+
+using namespace barrier;
+
+inline const Matcher<const IpcMessage&> IpcLogLineMessageEq(const String& s) {
+ const Matcher<const IpcLogLineMessage&> m(
+ Property(&IpcLogLineMessage::logLine, StrEq(s)));
+ return MatcherCast<const IpcMessage&>(m);
+}
+
+TEST(IpcLogOutputterTests, write_threadingEnabled_bufferIsSent)
+{
+ MockIpcServer mockServer;
+ mockServer.delegateToFake();
+
+ ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
+
+ EXPECT_CALL(mockServer, hasClients(_)).Times(AtLeast(3));
+ EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\n"), _)).Times(1);
+ EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\n"), _)).Times(1);
+
+ IpcLogOutputter outputter(mockServer, kIpcClientUnknown, true);
+ outputter.write(kNOTE, "mock 1");
+ mockServer.waitForSend();
+ outputter.write(kNOTE, "mock 2");
+ mockServer.waitForSend();
+}
+
+TEST(IpcLogOutputterTests, write_overBufferMaxSize_firstLineTruncated)
+{
+ MockIpcServer mockServer;
+
+ ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
+ EXPECT_CALL(mockServer, hasClients(_)).Times(1);
+ EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 2\nmock 3\n"), _)).Times(1);
+
+ IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false);
+ outputter.bufferMaxSize(2);
+
+ // log more lines than the buffer can contain
+ outputter.write(kNOTE, "mock 1");
+ outputter.write(kNOTE, "mock 2");
+ outputter.write(kNOTE, "mock 3");
+ outputter.sendBuffer();
+}
+
+TEST(IpcLogOutputterTests, write_underBufferMaxSize_allLinesAreSent)
+{
+ MockIpcServer mockServer;
+
+ ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
+
+ EXPECT_CALL(mockServer, hasClients(_)).Times(1);
+ EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1);
+
+ IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false);
+ outputter.bufferMaxSize(2);
+
+ // log more lines than the buffer can contain
+ outputter.write(kNOTE, "mock 1");
+ outputter.write(kNOTE, "mock 2");
+ outputter.sendBuffer();
+}
+
+// HACK: temporarily disable this intermittently failing unit test.
+// when the build machine is under heavy load, a race condition
+// usually happens.
+#if 0
+TEST(IpcLogOutputterTests, write_overBufferRateLimit_lastLineTruncated)
+{
+ MockIpcServer mockServer;
+
+ ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
+
+ EXPECT_CALL(mockServer, hasClients(_)).Times(2);
+ EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1);
+ EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 4\nmock 5\n"), _)).Times(1);
+
+ IpcLogOutputter outputter(mockServer, false);
+ outputter.bufferRateLimit(2, 1); // 1s
+
+ // log 1 more line than the buffer can accept in time limit.
+ outputter.write(kNOTE, "mock 1");
+ outputter.write(kNOTE, "mock 2");
+ outputter.write(kNOTE, "mock 3");
+
+ outputter.sendBuffer();
+
+ // after waiting the time limit send another to make sure
+ // we can log after the time limit passes.
+ // HACK: sleep causes the unit test to fail intermittently,
+ // so lets try 100ms (there must be a better way to solve this)
+ ARCH->sleep(2); // 2s
+ outputter.write(kNOTE, "mock 4");
+ outputter.write(kNOTE, "mock 5");
+ outputter.write(kNOTE, "mock 6");
+
+ outputter.sendBuffer();
+}
+#endif
+
+TEST(IpcLogOutputterTests, write_underBufferRateLimit_allLinesAreSent)
+{
+ MockIpcServer mockServer;
+
+ ON_CALL(mockServer, hasClients(_)).WillByDefault(Return(true));
+
+ EXPECT_CALL(mockServer, hasClients(_)).Times(2);
+ EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 1\nmock 2\n"), _)).Times(1);
+ EXPECT_CALL(mockServer, send(IpcLogLineMessageEq("mock 3\nmock 4\n"), _)).Times(1);
+
+ IpcLogOutputter outputter(mockServer, kIpcClientUnknown, false);
+ outputter.bufferRateLimit(4, 1); // 1s (should be plenty of time)
+
+ // log 1 more line than the buffer can accept in time limit.
+ outputter.write(kNOTE, "mock 1");
+ outputter.write(kNOTE, "mock 2");
+ outputter.sendBuffer();
+
+ // after waiting the time limit send another to make sure
+ // we can log after the time limit passes.
+ outputter.write(kNOTE, "mock 3");
+ outputter.write(kNOTE, "mock 4");
+ outputter.sendBuffer();
+}
+
+#endif // WINAPI_MSWINDOWS
diff --git a/src/test/unittests/platform/OSXKeyStateTests.cpp b/src/test/unittests/platform/OSXKeyStateTests.cpp
new file mode 100644
index 0000000..dd9e80f
--- /dev/null
+++ b/src/test/unittests/platform/OSXKeyStateTests.cpp
@@ -0,0 +1,57 @@
+/*
+ * barrier -- mouse and keyboard sharing utility
+ * Copyright (C) 2012-2016 Symless Ltd.
+ * Copyright (C) 2011 Nick Bolton
+ *
+ * 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/mock/barrier/MockKeyMap.h"
+#include "test/mock/barrier/MockEventQueue.h"
+#include "platform/OSXKeyState.h"
+
+#include "test/global/gtest.h"
+#include "test/global/gmock.h"
+
+TEST(OSXKeyStateTests, mapModifiersFromOSX_OSXMask_returnBarrierMask)
+{
+ barrier::KeyMap keyMap;
+ MockEventQueue eventQueue;
+ OSXKeyState keyState(&eventQueue, keyMap);
+
+ KeyModifierMask outMask = 0;
+
+ UInt32 shiftMask = 0 | kCGEventFlagMaskShift;
+ outMask = keyState.mapModifiersFromOSX(shiftMask);
+ EXPECT_EQ(KeyModifierShift, outMask);
+
+ UInt32 ctrlMask = 0 | kCGEventFlagMaskControl;
+ outMask = keyState.mapModifiersFromOSX(ctrlMask);
+ EXPECT_EQ(KeyModifierControl, outMask);
+
+ UInt32 altMask = 0 | kCGEventFlagMaskAlternate;
+ outMask = keyState.mapModifiersFromOSX(altMask);
+ EXPECT_EQ(KeyModifierAlt, outMask);
+
+ UInt32 cmdMask = 0 | kCGEventFlagMaskCommand;
+ outMask = keyState.mapModifiersFromOSX(cmdMask);
+ EXPECT_EQ(KeyModifierSuper, outMask);
+
+ UInt32 capsMask = 0 | kCGEventFlagMaskAlphaShift;
+ outMask = keyState.mapModifiersFromOSX(capsMask);
+ EXPECT_EQ(KeyModifierCapsLock, outMask);
+
+ UInt32 numMask = 0 | kCGEventFlagMaskNumericPad;
+ outMask = keyState.mapModifiersFromOSX(numMask);
+ EXPECT_EQ(KeyModifierNumLock, outMask);
+}