diff --git a/linux/debian/control b/linux/debian/control index fa4d0b760ed..b939901472a 100644 --- a/linux/debian/control +++ b/linux/debian/control @@ -13,6 +13,7 @@ Build-Depends: gir1.2-webkit2-4.1, ibus, libevdev-dev, + libglibmm-2.4-dev, libgtk-3-dev, libibus-1.0-dev (>= 1.2), libicu-dev, diff --git a/linux/mcompile/keymap/deadkey.cpp b/linux/mcompile/keymap/deadkey.cpp index b7b67daa25f..6b2e708034d 100644 --- a/linux/mcompile/keymap/deadkey.cpp +++ b/linux/mcompile/keymap/deadkey.cpp @@ -1,5 +1,5 @@ /* - * Keyman is copyright (C) 2004 - 2024 SIL International. MIT License. + * Keyman is copyright (C) SIL Global. MIT License. * * Mnemonic layout support for Linux */ diff --git a/linux/mcompile/keymap/deadkey.h b/linux/mcompile/keymap/deadkey.h index 5c52768ecf1..c125b0b3ab9 100644 --- a/linux/mcompile/keymap/deadkey.h +++ b/linux/mcompile/keymap/deadkey.h @@ -1,3 +1,8 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + * + * Mnemonic layout support for Linux + */ #pragma once #ifndef DEADKEY_H @@ -5,6 +10,13 @@ #include "mc_import_rules.h" #include +#include "keymap.h" + +struct KMX_DeadkeyMapping { // I4353 + KMX_WCHAR deadkey, dkid; + KMX_DWORD shift; + KMX_WORD vk; +}; /** @brief create a Vector of DeadKey containing all combinations of deadkey + character for ALL possible Linux keyboards */ std::vector create_deadkeys_by_basechar(); diff --git a/linux/mcompile/keymap/keymap.cpp b/linux/mcompile/keymap/keymap.cpp index f052a32a8f6..6a9a3d4e90f 100644 --- a/linux/mcompile/keymap/keymap.cpp +++ b/linux/mcompile/keymap/keymap.cpp @@ -1,5 +1,5 @@ /* - * Keyman is copyright (C) 2004 - 2024 SIL International. MIT License. + * Keyman is copyright (C) SIL Global. MIT License. * * Mnemonic layout support for Linux * diff --git a/linux/mcompile/keymap/keymap.h b/linux/mcompile/keymap/keymap.h index 74e67352b0a..1a84b5ac215 100644 --- a/linux/mcompile/keymap/keymap.h +++ b/linux/mcompile/keymap/keymap.h @@ -1,3 +1,9 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + * + * Mnemonic layout support for Linux + */ + #pragma once #ifndef KEYMAP_H #define KEYMAP_H diff --git a/linux/mcompile/keymap/main.cpp b/linux/mcompile/keymap/main.cpp new file mode 100644 index 00000000000..4ca4be8b957 --- /dev/null +++ b/linux/mcompile/keymap/main.cpp @@ -0,0 +1,89 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + * + * Created by Markus-SWAG on 2026-05-05 + * + * Mnemonic layout support for Linux + */ + +#include "mcompile.h" + +/** + * @brief main function for mcompile for Linux + * @param argc number of commandline arguments + * @param argv pointer to commandline arguments: executable, inputfile, outputfile + * @return 0 on success + */ + +int main(int argc, char* argv[]) { + + + int bDeadkeyConversion = 0; + + if (argc > 1) + bDeadkeyConversion = (strcmp(argv[1], "-d") == 0); + + int n = (bDeadkeyConversion ? 2 : 1); + + if (argc < 3 || argc > 4 || (argc - n) != 2) { + printf( + "Usage: \tmcompile [-d] infile.kmx outfile.kmx\n" + " \tmcompile converts a Keyman mnemonic layout to\n" + " \ta positional one based on the currently used \n" + " \tLinux keyboard layout\n" + " \t(-d convert deadkeys to plain keys) \n \n"); + + return 1; + } + + // -u option is not available for Linux and macOS + + KMX_CHAR* infile = argv[n]; + KMX_CHAR* outfile = argv[n + 1]; + + printf("mcompile%s \"%s\" \"%s\"\n", bDeadkeyConversion ? " -d" : "", infile, outfile); + + // 1. Load the keyman keyboard file + + // 2. For each key on the system layout, determine its output character and perform a + // 1-1 replacement on the keyman keyboard of that character with the base VK + shift + // state. This fixup will transform the char to a vk, which will avoid any issues + // with the key. + // + // + // For each deadkey, we need to determine its possible outputs. Then we generate a VK + // rule for that deadkey, e.g. [K_LBRKT] > dk(c101) + // + // Next, update each rule that references the output from that deadkey to add an extra + // context deadkey at the end of the context match, e.g. 'a' dk(c101) + [K_SPACE] > 'b'. + // This will require a memory layout change for the .kmx file, plus fixups on the + // context+output index offsets + // + // --> virtual character keys + // + // [CTRL ' '] : we look at the character, and replace it in the same way, but merely + // switch the shift state from the VIRTUALCHARKEY to VIRTUALKEY, without changing any + // other properties of the key. + // + // 3. Write the new keyman keyboard file + + LPKMX_KEYBOARD kmxfile = nullptr; + + if (!KMX_LoadKeyboard(infile, &kmxfile)) { + KMX_LogError(L"Failed to load keyboard (%d)\n", errno); + delete[] kmxfile; + return 3; + } + + if (KMX_DoConvert(kmxfile, bDeadkeyConversion, argc, (gchar**)argv)) { + if(!KMX_SaveKeyboard(kmxfile, outfile)) { + KMX_LogError(L"Failed to save keyboard (%d)\n", errno); + delete[] kmxfile; + return 3; + } + } + + delete[] kmxfile; + + return 0; +} diff --git a/linux/mcompile/keymap/mc_import_rules.cpp b/linux/mcompile/keymap/mc_import_rules.cpp index 9c376dafd6a..66aeb937671 100644 --- a/linux/mcompile/keymap/mc_import_rules.cpp +++ b/linux/mcompile/keymap/mc_import_rules.cpp @@ -1,5 +1,5 @@ /* - * Keyman is copyright (C) 2004 - 2024 SIL International. MIT License. + * Keyman is copyright (C) SIL Global. MIT License. * * Mnemonic layout support for Linux */ @@ -10,6 +10,9 @@ #include #include "mc_kmxfile.h" #include "keymap.h" +#include "deadkey.h" + +extern std::vector KMX_FDeadkeys; // I4353 const int KMX_ShiftStateMap[] = { ISVIRTUALKEY, diff --git a/linux/mcompile/keymap/mc_import_rules.h b/linux/mcompile/keymap/mc_import_rules.h index f955830eac3..c0df57f0dfd 100644 --- a/linux/mcompile/keymap/mc_import_rules.h +++ b/linux/mcompile/keymap/mc_import_rules.h @@ -1,8 +1,17 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + * + * Mnemonic layout support for Linux + */ + #pragma once #ifndef MC_IMPORT_RULES_H #define MC_IMPORT_RULES_H +#include +#include "km_types.h" + /** @brief Base class for Deadkey*/ class DeadKey { private: diff --git a/linux/mcompile/keymap/mc_kmxfile.cpp b/linux/mcompile/keymap/mc_kmxfile.cpp index 674da61fa1d..f045246e49e 100644 --- a/linux/mcompile/keymap/mc_kmxfile.cpp +++ b/linux/mcompile/keymap/mc_kmxfile.cpp @@ -1,11 +1,15 @@ /* - * Keyman is copyright (C) 2004 - 2024 SIL International. MIT License. + * Keyman is copyright (C) SIL Global. MIT License. * * Mnemonic layout support for Linux */ #include "mc_kmxfile.h" +#include #include +#include +#include + #define CERR_None 0x00000000 #define CERR_CannotAllocateMemory 0x00008004 @@ -576,9 +580,31 @@ FILE* Open_File(const KMX_CHAR* filename, const KMX_CHAR* mode) { return fopen(cpath.c_str(), (const KMX_CHAR*)mode); #else return fopen(filename, mode); - std::string cpath, cmode; - cpath = (const KMX_CHAR*)filename; - cmode = (const KMX_CHAR*)mode; - return fopen(cpath.c_str(), cmode.c_str()); #endif }; + +#define _countof(a) (sizeof(a) / sizeof(*(a))) + +/** + * @brief print (error) messages + * @param fmt text to print + */ +void KMX_LogError(const wchar_t* fmt, ...) { + wchar_t fmtbuf[256]; + const wchar_t* end = L"\0"; + const wchar_t* nl = L"\n"; + va_list vars; + int j = 0; + + va_start(vars, fmt); + vswprintf(fmtbuf, _countof(fmtbuf), fmt, vars); + fmtbuf[255] = 0; + + do { + putwchar(fmtbuf[j]); + j++; + } while (fmtbuf[j] != *end); + putwchar(*nl); + va_end(vars); +} + diff --git a/linux/mcompile/keymap/mc_kmxfile.h b/linux/mcompile/keymap/mc_kmxfile.h index f89a240a774..9af264939f1 100644 --- a/linux/mcompile/keymap/mc_kmxfile.h +++ b/linux/mcompile/keymap/mc_kmxfile.h @@ -1,10 +1,16 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + * + * Mnemonic layout support for Linux + */ + #pragma once #ifndef MC_KMXFILE_H #define MC_KMXFILE_H #include "km_types.h" #include -#include "mcompile.h" +#include #ifndef _KMXFILE_H #define _KMXFILE_H @@ -78,6 +84,9 @@ PKMX_WCHAR KMX_incxstr(PKMX_WCHAR p); /** @brief open a file */ FILE* Open_File(const KMX_CHAR* filename, const KMX_CHAR* mode); +/** @brief print (error) messages */ +void KMX_LogError(const wchar_t* fmt, ...); + #endif // _KMXFILE_H #endif /*MC_KMXFILE_H*/ diff --git a/linux/mcompile/keymap/mcompile.cpp b/linux/mcompile/keymap/mcompile.cpp index f8b96685b99..0f7219b5055 100644 --- a/linux/mcompile/keymap/mcompile.cpp +++ b/linux/mcompile/keymap/mcompile.cpp @@ -1,5 +1,5 @@ /* - * Keyman is copyright (C) SIL International. MIT License. + * Keyman is copyright (C) SIL Global. MIT License. * * Mnemonic layout support for linux * @@ -10,13 +10,13 @@ */ #include "mcompile.h" +#include "keymap.h" +#include "deadkey.h" + const int nr_DK_pairs = 1000; static const int size_DK_array = (nr_DK_pairs + 1) *3; -/** @brief convert mnemonic keyboard layout to positional keyboard layout and translate keyboard */ -KMX_BOOL KMX_DoConvert(LPKMX_KEYBOARD kbd, KMX_BOOL bDeadkeyConversion, gint argc, gchar* argv[]); - /** @brief Collect the key data, translate it to kmx and append to the existing keyboard */ bool KMX_ImportRules(LPKMX_KEYBOARD kp, vec_dword_3D& all_vector, GdkKeymap** keymap, std::vector* KMX_FDeadkeys, KMX_BOOL bDeadkeyConversion); // I4353 // I4327 @@ -25,85 +25,6 @@ int KMX_GetDeadkeys(vec_dword_2D& dk_Table, KMX_WORD deadkey, std::vector KMX_FDeadkeys; // I4353 -#define _countof(a) (sizeof(a) / sizeof(*(a))) - -/** - * @brief main function for mcompile for Linux - * @param argc number of commandline arguments - * @param argv pointer to commandline arguments: executable, inputfile, outputfile - * @return 0 on success - */ - - int main(int argc, char* argv[]) { - - - int bDeadkeyConversion = 0; - - if (argc > 1) - bDeadkeyConversion = (strcmp(argv[1], "-d") == 0); // I4552 - - int n = (bDeadkeyConversion ? 2 : 1); - - if (argc < 3 || argc > 4 || (argc - n) != 2) { // I4273// I4273 - printf( - "Usage: \tmcompile [-d] infile.kmx outfile.kmx\n" - " \tmcompile converts a Keyman mnemonic layout to\n" - " \ta positional one based on the currently used \n" - " \tLinux keyboard layout\n" - " \t(-d convert deadkeys to plain keys) \n \n"); // I4552 - - return 1; - } - - // -u option is not available for Linux and macOS - - KMX_CHAR* infile = argv[n]; - KMX_CHAR* outfile = argv[n + 1]; - - printf("mcompile%s \"%s\" \"%s\"\n", bDeadkeyConversion ? " -d" : "", infile, outfile); // I4174 - - // 1. Load the keyman keyboard file - - // 2. For each key on the system layout, determine its output character and perform a - // 1-1 replacement on the keyman keyboard of that character with the base VK + shift - // state. This fixup will transform the char to a vk, which will avoid any issues - // with the key. - // - // - // For each deadkey, we need to determine its possible outputs. Then we generate a VK - // rule for that deadkey, e.g. [K_LBRKT] > dk(c101) - // - // Next, update each rule that references the output from that deadkey to add an extra - // context deadkey at the end of the context match, e.g. 'a' dk(c101) + [K_SPACE] > 'b'. - // This will require a memory layout change for the .kmx file, plus fixups on the - // context+output index offsets - // - // --> virtual character keys - // - // [CTRL ' '] : we look at the character, and replace it in the same way, but merely - // switch the shift state from the VIRTUALCHARKEY to VIRTUALKEY, without changing any - // other properties of the key. - // - // 3. Write the new keyman keyboard file - - LPKMX_KEYBOARD kmxfile; - - if (!KMX_LoadKeyboard(infile, &kmxfile)) { - KMX_LogError(L"Failed to load keyboard (%d)\n", errno); - return 3; - } - - if (KMX_DoConvert(kmxfile, bDeadkeyConversion, argc, (gchar**)argv)) { // I4552F - if(!KMX_SaveKeyboard(kmxfile, outfile)) { - KMX_LogError(L"Failed to save keyboard (%d)\n", errno); - return 3; - } - } - - delete kmxfile; - return 0; -} - // Map of all shift states that we will work with const KMX_DWORD VKShiftState[] = {0, K_SHIFTFLAG, LCTRLFLAG | RALTFLAG, K_SHIFTFLAG | LCTRLFLAG | RALTFLAG, 0xFFFF}; @@ -555,24 +476,3 @@ int KMX_GetDeadkeys(vec_dword_2D& dk_Table, KMX_WORD deadkey, std::vector -#include "keymap.h" -#include "deadkey.h" +#include #include "mc_kmxfile.h" -struct KMX_DeadkeyMapping { // I4353 - KMX_WCHAR deadkey, dkid; - KMX_DWORD shift; - KMX_WORD vk; -}; - -extern std::vector KMX_FDeadkeys; // I4353 - -/** @brief print (error) messages */ -void KMX_LogError(const wchar_t* fmt, ...); +/** @brief convert mnemonic keyboard layout to positional keyboard layout and translate keyboard */ +KMX_BOOL KMX_DoConvert(LPKMX_KEYBOARD kbd, KMX_BOOL bDeadkeyConversion, gint argc, gchar* argv[]); #endif /*MCOMPILE_H*/ diff --git a/linux/mcompile/keymap/meson.build b/linux/mcompile/keymap/meson.build index af9c84017e6..9c8fd506964 100644 --- a/linux/mcompile/keymap/meson.build +++ b/linux/mcompile/keymap/meson.build @@ -14,6 +14,7 @@ deps = [gtk, xkb] subdir('resources') cpp_files = files( + 'main.cpp', 'keymap.cpp', 'deadkey.cpp', 'mcompile.cpp', @@ -23,13 +24,25 @@ cpp_files = files( '../../../common/cpp/utfcodec.cpp', ) -comon_include_dir = [ - include_directories('../../../common/include') +test_files = files( + 'keymap.cpp', + 'deadkey.cpp', + 'mc_kmxfile.cpp', + 'mcompile.cpp', + 'mc_import_rules.cpp', + '../../../common/cpp/km_u16.cpp', + '../../../common/cpp/utfcodec.cpp', +) + +common_include_dir = [ + include_directories('../../../common/include','.'), ] mcompile = executable( 'mcompile', sources: [cpp_files], dependencies: deps, - include_directories : comon_include_dir + include_directories : common_include_dir ) + +subdir('test') diff --git a/linux/mcompile/keymap/subprojects/.gitignore b/linux/mcompile/keymap/subprojects/.gitignore new file mode 100644 index 00000000000..88fb1651218 --- /dev/null +++ b/linux/mcompile/keymap/subprojects/.gitignore @@ -0,0 +1,4 @@ +/*.zip +/*.tgz +/packagecache +/googletest-* diff --git a/linux/mcompile/keymap/subprojects/gtest.wrap b/linux/mcompile/keymap/subprojects/gtest.wrap new file mode 100644 index 00000000000..9902a4f7ecd --- /dev/null +++ b/linux/mcompile/keymap/subprojects/gtest.wrap @@ -0,0 +1,16 @@ +[wrap-file] +directory = googletest-1.17.0 +source_url = https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz +source_filename = googletest-1.17.0.tar.gz +source_hash = 65fab701d9829d38cb77c14acdc431d2108bfdbf8979e40eb8ae567edf10b27c +patch_filename = gtest_1.17.0-4_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/gtest_1.17.0-4/get_patch +patch_hash = 3abf7662d09db706453a5b064a1e914678c74b9d9b0b19382747ca561d0d8750 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/gtest_1.17.0-4/googletest-1.17.0.tar.gz +wrapdb_version = 1.17.0-4 + +[provide] +gtest = gtest_dep +gtest_main = gtest_main_dep +gmock = gmock_dep +gmock_main = gmock_main_dep diff --git a/linux/mcompile/keymap/test/keymap.tests.cpp b/linux/mcompile/keymap/test/keymap.tests.cpp new file mode 100644 index 00000000000..e1d3175b12b --- /dev/null +++ b/linux/mcompile/keymap/test/keymap.tests.cpp @@ -0,0 +1,522 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + * + * Mnemonic layout support for Linux + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mcompile.h" +#include "keymap.h" + +class TestDataValues { +protected: + guint keycode; + KMX_WCHAR expected_char; + std::string layout; + guint shiftstate; + KMX_WCHAR expected_deadkey_value; + +public: + TestDataValues(guint k, KMX_WCHAR e, std::string l, guint s) : keycode(k), expected_char(e), layout(l), shiftstate(s) { + } + + guint get_keycode() { + return keycode; + } + + KMX_WCHAR get_expected_char() { + return expected_char; + } + + std::string get_layout() { + return layout; + } + + guint get_shiftstate() { + return shiftstate; + } + + +}; + + +class KeyboardTestDataValues : public TestDataValues { +public: + KeyboardTestDataValues(guint k, KMX_WCHAR e, std::string l, guint s, KMX_WCHAR d) : TestDataValues(k, e, l, s), expected_deadkey_value(d) { + } + + KMX_WCHAR get_expected_deadkey_value() { + return expected_deadkey_value; + } + +protected: + KMX_WCHAR expected_deadkey_value; +}; + +class KeyboardTestParameters { +public: + KeyboardTestParameters(std::vector e, std::string l, guint s, std::vector d) : expected_keysyms(e), layout(l), shiftstate(s), expected_deadkey_values(d) { + generate_test_data_values(); + } + + std::vector get_test_data() { + return test_data_values; + } + +protected: + std::vector expected_keysyms; + std::vector test_data_values = {}; + std::string layout; + guint shiftstate; + std::vector expected_deadkey_values = {}; + std::vector keycodes = {38, 56, 54, 40, 26, 41, 42, 43, 31, 44, + 45, 46, 58, 57, 32, 33, 24, 27, 39, 28, + 30, 55, 25, 53, 29, 52, 19, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 65, 49, 20, 21, + 34, 35, 51, 47, 48, 59, 60, 61, 123, 94}; + + void generate_test_data_values() { + EXPECT_EQ(keycodes.size(), expected_keysyms.size()) << "Keycodes and expected keysyms vectors must be of the same size."; + EXPECT_EQ(keycodes.size(), expected_deadkey_values.size()) << "Keycodes and expected deadkey values vectors must be of the same size."; + for (uint k = 0; k < keycodes.size() && k < expected_keysyms.size(); k++) { + test_data_values.emplace_back(KeyboardTestDataValues(keycodes[k], expected_keysyms[k], layout, shiftstate, expected_deadkey_values[k])); + } + } +}; + +class KeyboardConversionTest : public testing::Test { +public: +protected: + GdkKeymap* test_keymap; + GdkDisplay* test_display; + gint argc = 0; + char** argv = nullptr; + std::string default_layout; + +private: + void initialize_keymap() { + gdk_init(&argc, &argv); + test_display = nullptr; + test_keymap = nullptr; + test_display = gdk_display_get_default(); + ASSERT_NE(test_display, nullptr) << "ERROR: can't get display"; + test_keymap = gdk_keymap_get_for_display(test_display); + ASSERT_NE(test_keymap, nullptr) << "ERROR: Can't get keymap"; + } + + void retrieve_default_layout() { + std::vector> sources; + + Gio::init(); + auto settings = Gio::Settings::create("org.gnome.desktop.input-sources"); + Glib::VariantBase base; + settings->get_value("sources", base); + using SourcesVariant = Glib::Variant>>; + auto variant = Glib::VariantBase::cast_dynamic(base); + sources = variant.get(); + + if (sources.empty()) { + GTEST_SKIP() << "ERROR: No input sources found"; + } + + const auto& [type, system_layout] = sources[0]; + + if (type == "xkb") { + if (system_layout == "de" || system_layout == "us") { + default_layout = system_layout; + } else { + GTEST_SKIP() << "Default layout is not DE or US."; + } + } else { + GTEST_SKIP() << "Default input source type is not xkb."; + } + } + + void SetUp() override { + initialize_keymap(); + retrieve_default_layout(); + if (!GDK_IS_X11_DISPLAY(test_display)) { + GTEST_SKIP() << "Not running on X11 display, skipping tests that require X11 keymap functionality."; + } + } + + void TearDown() override { + default_layout.clear(); + argc = 0; + free(argv); + argv = nullptr; + if (test_display) { + gdk_display_close(test_display); + test_display = nullptr; + } + } +}; + +class GetKeyValUnderlyingFromKeyCodeUnderlyingTest : public KeyboardConversionTest, + public testing::WithParamInterface {}; + +TEST_P(GetKeyValUnderlyingFromKeyCodeUnderlyingTest, KmxGetKeyValUnderlyingFromKeyCodeUnderlying) { + guint keycode; + KMX_WCHAR expected_char; + std::string test_layout; + guint shiftstate; + KMX_WCHAR expected_deadkey_value; + KeyboardTestDataValues parameter = GetParam(); + + keycode = parameter.get_keycode(); + expected_char = parameter.get_expected_char(); + test_layout = parameter.get_layout(); + shiftstate = parameter.get_shiftstate(); + expected_deadkey_value = parameter.get_expected_deadkey_value(); + + std::cout << "Testing keycode: " << keycode << " expecting char: " << expected_char << " with layout: " << test_layout + << " and shiftstate: " << shiftstate << std::endl; + if (test_layout != default_layout) { + GTEST_SKIP() << "Default layout is not " << default_layout << "."; + } + + KMX_WCHAR deadkey = 0; + KMX_WCHAR result = KMX_get_KeyValUnderlying_From_KeyCodeUnderlying(test_keymap, keycode, shiftstate, &deadkey); + EXPECT_EQ(result, expected_char) << "Failed for keycode: " << keycode; + EXPECT_EQ(deadkey, expected_deadkey_value) << "Failed for keycode: " << keycode; +} + +INSTANTIATE_TEST_SUITE_P( + BaseUs, + GetKeyValUnderlyingFromKeyCodeUnderlyingTest, + testing::ValuesIn(KeyboardTestParameters( + {u'a', u'b', u'c', u'd', u'e', u'f', u'g', u'h', u'i', u'j', + u'k', u'l', u'm', u'n', u'o', u'p', u'q', u'r', u's', u't', + u'u', u'v', u'w', u'x', u'y', u'z', u'0', u'1', u'2', u'3', + u'4', u'5', u'6', u'7', u'8', u'9', u' ', u'`', u'-', u'=', + u'[', u']', u'\\', u';', u'\'', u',', u'.', u'/', u'\000', u'<'}, + "us", + 0, + {0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0}) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + ShiftUs, + GetKeyValUnderlyingFromKeyCodeUnderlyingTest, + testing::ValuesIn(KeyboardTestParameters( + {u'A', u'B', u'C', u'D', u'E', u'F', u'G', u'H', u'I', u'J', + u'K', u'L', u'M', u'N', u'O', u'P', u'Q', u'R', u'S', u'T', + u'U', u'V', u'W', u'X', u'Y', u'Z', u')', u'!', u'@', u'#', + u'$', u'%', u'^', u'&', u'*', u'(', u' ', u'~', u'_', u'+', + u'{', u'}', u'|', u':', u'"', u'<', u'>', u'?', u'\000', u'>'}, + "us", + K_SHIFTFLAG, + {0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0}) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + AltGrUs, + GetKeyValUnderlyingFromKeyCodeUnderlyingTest, + testing::ValuesIn(KeyboardTestParameters( + {u'a', u'b', u'c', u'd', u'e', u'f',u'g', u'h', u'i', u'j', + u'k', u'l', u'm', u'n', u'o', u'p', u'q', u'r', u's', u't', + u'u', u'v', u'w', u'x', u'y', u'z', u'0', u'1', u'2', u'3', + u'4', u'5', u'6', u'7', u'8', u'9', u'\000', u'`', u'-', u'=', + u'[', u']', u'\\', u';', u'\'', u',', u'.', u'/', u'\000', u'|'}, + "us", + (LCTRLFLAG | RALTFLAG), + {0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0}) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + ShiftAltGrUs, + GetKeyValUnderlyingFromKeyCodeUnderlyingTest, + testing::ValuesIn(KeyboardTestParameters( + {u'A', u'B', u'C', u'D', u'E', u'F', u'G', u'H', u'I', u'J', + u'K', u'L', u'M', u'N', u'O', u'P', u'Q', u'R', u'S', u'T', + u'U', u'V', u'W', u'X', u'Y', u'Z', u')', u'!', u'@', u'#', + u'$', u'%', u'^', u'&', u'*', u'(', u'\000', u'~', u'_', u'+', + u'{', u'}', u'|', u':', u'"', u'<', u'>', u'?', u'\000', u'¦'}, + "us", + (K_SHIFTFLAG | LCTRLFLAG | RALTFLAG), + {0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0}) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + BaseDe, + GetKeyValUnderlyingFromKeyCodeUnderlyingTest, + testing::ValuesIn(KeyboardTestParameters( + {u'a', u'b', u'c', u'd', u'e', u'f', u'g', u'h', u'i', u'j', + u'k', u'l', u'm', u'n', u'o', u'p', u'q', u'r', u's', u't', + u'u', u'v', u'w', u'x', u'z', u'y', u'0', u'1', u'2', u'3', + u'4', u'5', u'6', u'7', u'8', u'9', u' ', u'\xffff', u'ß', u'\xffff', + u'ü', u'+', u'#', u'ö', u'ä', u',', u'.', u'-', u'\000', u'<'}, + "de", + 0, + {0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,u'^',0,u'´', + 0,0,0,0,0,0,0,0,0,0}) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + ShiftDe, + GetKeyValUnderlyingFromKeyCodeUnderlyingTest, + testing::ValuesIn(KeyboardTestParameters( + {u'A', u'B', u'C', u'D', u'E', u'F', u'G', u'H', u'I', u'J', + u'K', u'L', u'M', u'N', u'O', u'P', u'Q', u'R', u'S', u'T', + u'U', u'V', u'W', u'X', u'Z', u'Y', u'=', u'!', u'"', u'§', + u'$', u'%', u'&', u'/', u'(', u')', u' ', u'°', u'?', u'\xffff', + u'Ü', u'*', u'\'', u'Ö', u'Ä', u';', u':', u'_', u'\000', u'>'}, + "de", + K_SHIFTFLAG, + {0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,u'`', + 0,0,0,0,0,0,0,0,0,0}) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + AltGrDe, + GetKeyValUnderlyingFromKeyCodeUnderlyingTest, + testing::ValuesIn(KeyboardTestParameters( + {u'æ', u'\xfffe', u'¢', u'ð', u'\xfffe', u'\xfffe', u'\xfffe', u'\xfffe', u'\xfffe', u'\xffff', + u'\xfffe', u'\xfffe', u'µ', u'\xfffe', u'ø', u'þ', u'@', u'¶', u'\xfffe', u'\xfffe', + u'\xfffe', u'\xfffe', u'\xfffe', u'«', u'\xfffe', u'»', u'}', u'¹', u'²', u'³', + u'¼', u'½', u'¬', u'{', u'[', u']', u'\000', u'\xfffe', u'\\', u'\xffff', + u'\xffff', u'~', u'\xfffe', u'\xffff', u'\xffff', u'·', u'\xfffe', u'\xfffe', u'\000', u'|'}, + "de", + (LCTRLFLAG | RALTFLAG), + {0,0,0,0,0,0,0,0,0,u'\000', + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,u'¸', + u'¨',0,0,u'˝',u'^',0,0,0,0,0}) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + ShiftAltGrDe, + GetKeyValUnderlyingFromKeyCodeUnderlyingTest, + testing::ValuesIn(KeyboardTestParameters( + {u'Æ', u'\xfffe', u'©', u'Ð', u'\xfffe', u'ª', u'\xfffe', u'\xfffe', u'\xfffe', u'\xffff', + u'&', u'\xfffe', u'º', u'\xfffe', u'Ø', u'Þ', u'\xfffe', u'®', u'\xfffe', u'\xfffe', + u'\xfffe', u'\xfffe', u'§', u'\xfffe', u'¥', u'\xfffe', u'°', u'¡', u'\xfffe', u'£', + u'¤', u'\xfffe', u'\xfffe', u'\xfffe', u'\xfffe', u'±', u'\000', u'\xfffe', u'¿', u'\xffff', + u'\xffff', u'¯', u'\xffff', u'\xffff', u'\xffff', u'×', u'÷', u'\xfffe', u'\000', u'\xffff'}, + "de", + (K_SHIFTFLAG | LCTRLFLAG | RALTFLAG), + {0,0,0,0,0,0,0,0,0,u'˙', + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,u'˛', + u'˚',0,u'˘',u'\000',u'ˇ',0,0,0,0,u'\000'}) + .get_test_data())); + +class GetKeyValFromKeyCodeTestDataValues : public TestDataValues { +public: + GetKeyValFromKeyCodeTestDataValues(guint k, KMX_WCHAR e, std::string l, guint s, int c) : TestDataValues(k, e, l, s), caps(c) { + } + + int get_caps() { + return caps; + } + +protected: + int caps; +}; + +class GetKeyValFromKeyCodeTestParameters { +public: + GetKeyValFromKeyCodeTestParameters(std::vector e, std::string l, guint s, int c) + : expected_keysyms(e), layout(l), shiftstate(s), caps(c) { + generate_test_data_values(); + } + + std::vector get_test_data() { + return test_data_values; + } + +protected: + std::vector expected_keysyms; + std::vector test_data_values = {}; + std::string layout; + guint shiftstate; + int caps; + std::vector keycodes = {38, 56, 54, 40, 26, 41, 42, 43, 31, 44, + 45, 46, 58, 57, 32, 33, 24, 27, 39, 28, + 30, 55, 25, 53, 29, 52, 19, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 65, 49, 20, 21, + 34, 35, 51, 47, 48, 59, 60, 61, 123, 94}; + + void generate_test_data_values() { + EXPECT_EQ(keycodes.size(), expected_keysyms.size()) << "Keycodes and expected keysyms vectors must be of the same size."; + for (guint k = 0; k < keycodes.size() && k < expected_keysyms.size(); k++) { + test_data_values.emplace_back( + GetKeyValFromKeyCodeTestDataValues(keycodes[k], expected_keysyms[k], layout, shiftstate, caps)); + } + } +}; + +class GetKeyValFromKeyCodeTest : public KeyboardConversionTest, + public testing::WithParamInterface {}; + +TEST_P(GetKeyValFromKeyCodeTest, kmxGetKeyValFromKeyCode) { + guint keycode; + KMX_WCHAR expected_char; + std::string test_layout; + guint shiftstate; + int caps; + + GetKeyValFromKeyCodeTestDataValues parameter = GetParam(); + + keycode = parameter.get_keycode(); + expected_char = parameter.get_expected_char(); + test_layout = parameter.get_layout(); + shiftstate = parameter.get_shiftstate(); + caps = parameter.get_caps(); + + std::cout << "Testing keycode: " << keycode << " expecting char: " << expected_char << " with layout: " << test_layout + << " and shiftstate: " << shiftstate << " caps: " << caps << std::endl; + if (test_layout != default_layout) { + GTEST_SKIP() << "Default layout is not " << default_layout << "."; + } + + KMX_WCHAR keyV = + KMX_get_KeyVal_From_KeyCode(test_keymap, keycode, ShiftState(convert_Shiftstate_to_LinuxShiftstate(shiftstate)), caps); + EXPECT_EQ(keyV, expected_char) << "Failed for keycode: " << keycode << " keyval: " << ((KMX_WCHAR)keyV) << " expected_char:" << ((int)expected_char); +} + +INSTANTIATE_TEST_SUITE_P( + BaseUs, + GetKeyValFromKeyCodeTest, + testing::ValuesIn(GetKeyValFromKeyCodeTestParameters( + {u'a', u'b', u'c', u'd', u'e', u'f', u'g', u'h', u'i', u'j', + u'k', u'l', u'm', u'n', u'o', u'p', u'q', u'r', u's', u't', + u'u', u'v', u'w', u'x', u'y', u'z', u'0', u'1', u'2', u'3', + u'4', u'5', u'6', u'7', u'8', u'9', u' ', u'`', u'-', u'=', + u'[', u']', u'\\', u';', u'\'', u',', u'.', u'/', u'\000', u'<'}, + "us", + 0, + 0) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + ShiftUs, + GetKeyValFromKeyCodeTest, + testing::ValuesIn(GetKeyValFromKeyCodeTestParameters( + {u'A', u'B', u'C', u'D', u'E', u'F', u'G', u'H', u'I', u'J', + u'K', u'L', u'M', u'N', u'O', u'P', u'Q', u'R', u'S', u'T', + u'U', u'V', u'W', u'X', u'Y', u'Z', u')', u'!', u'@', u'#', + u'$', u'%', u'^', u'&', u'*', u'(', u' ', u'~', u'_', u'+', + u'{', u'}', u'|', u':', u'"', u'<', u'>', u'?', u'\000', u'>'}, + "us", + K_SHIFTFLAG, + 0) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + AltGrUs, + GetKeyValFromKeyCodeTest, + testing::ValuesIn(GetKeyValFromKeyCodeTestParameters( + {u'a', u'b', u'c', u'd', u'e', u'f', u'g', u'h', u'i', u'j', + u'k', u'l', u'm', u'n', u'o', u'p', u'q', u'r', u's', u't', + u'u', u'v', u'w', u'x', u'y', u'z', u'0', u'1', u'2', u'3', + u'4', u'5', u'6', u'7', u'8', u'9', u'\000', u'`', u'-', u'=', + u'[', u']', u'\\', u';', u'\'', u',', u'.', u'/', u'\000', u'|'}, + "us", + (LCTRLFLAG | RALTFLAG), + 0) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + ShiftAltGrUs, + GetKeyValFromKeyCodeTest, + testing::ValuesIn(GetKeyValFromKeyCodeTestParameters( + {u'A', u'B', u'C', u'D', u'E', u'F', u'G', u'H', u'I', u'J', + u'K', u'L', u'M', u'N', u'O', u'P', u'Q', u'R', u'S', u'T', + u'U', u'V', u'W', u'X', u'Y', u'Z', u')', u'!', u'@', u'#', + u'$', u'%', u'^', u'&', u'*', u'(', u'\000', u'~', u'_', u'+', + u'{', u'}', u'|', u':', u'"', u'<', u'>', u'?', u'\000', u'¦'}, + "us", + (K_SHIFTFLAG | LCTRLFLAG | RALTFLAG), + 0) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + BaseDe, + GetKeyValFromKeyCodeTest, + testing::ValuesIn(GetKeyValFromKeyCodeTestParameters( + {u'a', u'b', u'c', u'd', u'e', u'f', u'g', u'h', u'i', u'j', + u'k', u'l', u'm', u'n', u'o', u'p', u'q', u'r', u's', u't', + u'u', u'v', u'w', u'x', u'z', u'y', u'0', u'1', u'2', u'3', + u'4', u'5', u'6', u'7', u'8', u'9', u' ', u'﹒', u'ß', u'﹑', + u'ü', u'+', u'#', u'ö', u'ä', u',', u'.', u'-', u'\000', u'<'}, + "de", + 0, + 0) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + ShiftDe, + GetKeyValFromKeyCodeTest, + testing::ValuesIn(GetKeyValFromKeyCodeTestParameters( + {u'A', u'B', u'C', u'D', u'E', u'F', u'G', u'H', u'I', u'J', + u'K', u'L', u'M', u'N', u'O', u'P', u'Q', u'R', u'S', u'T', + u'U', u'V', u'W', u'X', u'Z', u'Y', u'=', u'!', u'"', u'§', + u'$', u'%', u'&', u'/', u'(', u')', u' ', u'°', u'?', u'﹐', + u'Ü', u'*', u'\'', u'Ö', u'Ä', u';', u':', u'_', u'\000', u'>'}, + "de", + K_SHIFTFLAG, + 0) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + AltGrDe, + GetKeyValFromKeyCodeTest, + testing::ValuesIn(GetKeyValFromKeyCodeTestParameters( + {u'æ', u'\xad2', u'¢', u'ð', u'€', u'ǰ', u'ο', u'ʱ', u'ࣽ', u'﹠', + u'\x3a2', u'Ƴ', u'µ', u'\xad3', u'ø', u'þ', u'@', u'¶', u'ſ', u'μ', + u'ࣾ', u'૾', u'ſ', u'«', u'ࣻ', u'»', u'}', u'¹', u'²', u'³', + u'¼', u'½', u'¬', u'{', u'[', u']', u'\000', u'′', u'\\', u'﹛', + u'﹗', u'~', u'\xad1', u'﹙', u'﹒', u'·', u'…', u'પ', u'\000', u'|'}, + "de", + (LCTRLFLAG | RALTFLAG), + 0) + .get_test_data())); + +INSTANTIATE_TEST_SUITE_P( + ShiftAltGrDe, + GetKeyValFromKeyCodeTest, + testing::ValuesIn(GetKeyValFromKeyCodeTestParameters( + {u'Æ', u'ૐ', u'©', u'Ð', u'€', u'ª', u'ν', u'ʡ', u'ʹ', u'﹖', + u'&', u'ƣ', u'º', u'\xad1', u'Ø', u'Þ', u'ߙ', u'®', u'ẞ', u'ά', + u'ࣼ', u'૽', u'§', u'‹', u'¥', u'›', u'°', u'¡', u'ૃ', u'£', + u'¤', u'ૄ', u'ૅ', u'\xac6', u'ૉ', u'±', u'\000', u'″', u'¿', u'﹜', + u'﹘', u'¯', u'﹕', u'﹠', u'﹚', u'×', u'÷', u'\xaa9', u'\000', u'﹨'}, + "de", + (K_SHIFTFLAG | LCTRLFLAG | RALTFLAG), + 0) + .get_test_data())); diff --git a/linux/mcompile/keymap/test/meson.build b/linux/mcompile/keymap/test/meson.build new file mode 100644 index 00000000000..ac467b957fc --- /dev/null +++ b/linux/mcompile/keymap/test/meson.build @@ -0,0 +1,15 @@ +giomm_dep = dependency('giomm-2.4') +glibmm_dep = dependency('glibmm-2.4') + +gtest = subproject('gtest') + +gtest_main_dep = gtest.get_variable('gtest_main_dep') + +keymaptest = executable('keymaptest', + sources: ['keymap.tests.cpp', test_files], + include_directories: [common_include_dir], + include_directories: [common_include_dir], + dependencies: [ gtest_main_dep, gtk, xkb, giomm_dep, glibmm_dep ], + ) + +test('keymaptest', keymaptest)