Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
99baacb
adding unit tests for Linux mcompile
Markus-SWAG Apr 28, 2026
77acb6f
adding dependency to libglibmm-2.4-dev
Markus-SWAG Apr 28, 2026
c040314
adding dependency to libglibmm-2.4-dev typo
Markus-SWAG Apr 28, 2026
0ed8eb1
Prevent running unit tests on Wayland
Markus-SWAG Apr 28, 2026
f309d90
incorporating review results for adding unit tests
Markus-SWAG May 5, 2026
8bd47e4
parametrized unit tests for mcompile
Markus-SWAG May 6, 2026
d5accca
Update linux/mcompile/keymap/deadkey.h
Markus-SWAG May 6, 2026
6e38fc0
renamed files according test standards
Markus-SWAG May 6, 2026
c05bba9
Merge remote-tracking branch 'refs/remotes/origin/feat/linux/mnemonic…
Markus-SWAG May 6, 2026
bb87e1d
substituting SIL International by SIL Global in all file headers
Markus-SWAG May 6, 2026
0646808
erasing year from the file headers
Markus-SWAG May 6, 2026
e3dd7f1
added delete kmxfile as before
Markus-SWAG May 6, 2026
8ba6a13
added 1 more file header
Markus-SWAG May 6, 2026
9cf4e78
fixing typo
Markus-SWAG May 6, 2026
471c6c4
adding test suite for KMX_get_KeyVal_From_KeyCode
Markus-SWAG May 7, 2026
61f1a2b
incorporating review results
Markus-SWAG May 7, 2026
8803089
reformatting test parameters
Markus-SWAG May 7, 2026
c9c45a0
German Unit test now alos successfully finishing
Markus-SWAG May 11, 2026
0ccc9af
Update linux/mcompile/keymap/test/meson.build
Markus-SWAG May 11, 2026
8cf0c41
Update linux/mcompile/keymap/mc_kmxfile.cpp
Markus-SWAG May 11, 2026
cc9e0be
Update linux/mcompile/keymap/deadkey.h
Markus-SWAG May 11, 2026
7b367e9
Update linux/mcompile/keymap/main.cpp
Markus-SWAG May 11, 2026
28b0c96
changes after review
Markus-SWAG May 11, 2026
fcea563
improving tests, check on deadkey variable
Markus-SWAG May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions linux/debian/control
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion linux/mcompile/keymap/deadkey.cpp
Original file line number Diff line number Diff line change
@@ -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
*/
Expand Down
12 changes: 12 additions & 0 deletions linux/mcompile/keymap/deadkey.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Mnemonic layout support for Linux
*/

#pragma once
#ifndef DEADKEY_H
#define DEADKEY_H

#include "mc_import_rules.h"
#include <map>
#include <keymap.h>
Comment thread
Markus-SWAG marked this conversation as resolved.
Outdated

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<DeadKey*> create_deadkeys_by_basechar();
Expand Down
2 changes: 1 addition & 1 deletion linux/mcompile/keymap/keymap.cpp
Original file line number Diff line number Diff line change
@@ -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
*
Expand Down
6 changes: 6 additions & 0 deletions linux/mcompile/keymap/keymap.h
Original file line number Diff line number Diff line change
@@ -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
Expand Down
89 changes: 89 additions & 0 deletions linux/mcompile/keymap/main.cpp
Comment thread
ermshiperete marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -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"
Comment thread
ermshiperete marked this conversation as resolved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From devin.ai:

main.cpp relies on transitive includes for strcmp, printf, and errno

main.cpp uses strcmp (line 24), printf (lines 29, 44), and errno (lines 73, 80) but only directly includes mcompile.h. These symbols are available through transitive includes: mcompile.h<gdk/gdk.h><glib.h> → various C standard headers. This works but is fragile — a change in GDK's internal includes could break compilation. The old code had the same issue. Consider adding explicit #include <cstring>, #include <cstdio>, and #include <cerrno>.


/**
* @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);
Comment thread
ermshiperete marked this conversation as resolved.
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);
Comment thread
ermshiperete marked this conversation as resolved.
delete kmxfile;
return 3;
}
}

delete kmxfile;
Comment thread
Markus-SWAG marked this conversation as resolved.
Outdated

return 0;
Comment on lines +78 to +88
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From devin.ai:

Program returns success (0) when KMX_DoConvert fails — pre-existing issue

In main.cpp:78-86, when KMX_DoConvert returns FALSE, the code falls through to delete[] kmxfile; return 0;, returning success even though conversion failed. This is the same behavior as the old code in mcompile.cpp and is consistent with the comment at mcompile.cpp:8-9 that the program 'deliberately leaks memory as it has a very short life cycle.' However, a non-zero exit code would be more appropriate for a failed conversion.

}
5 changes: 4 additions & 1 deletion linux/mcompile/keymap/mc_import_rules.cpp
Original file line number Diff line number Diff line change
@@ -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
*/
Expand All @@ -10,6 +10,9 @@
#include <stdio.h>
#include "mc_kmxfile.h"
#include "keymap.h"
#include "deadkey.h"

extern std::vector<KMX_DeadkeyMapping> KMX_FDeadkeys; // I4353

const int KMX_ShiftStateMap[] = {
ISVIRTUALKEY,
Expand Down
9 changes: 9 additions & 0 deletions linux/mcompile/keymap/mc_import_rules.h
Original file line number Diff line number Diff line change
@@ -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 <vector>
#include "km_types.h"

/** @brief Base class for Deadkey*/
class DeadKey {
private:
Expand Down
31 changes: 30 additions & 1 deletion linux/mcompile/keymap/mc_kmxfile.cpp
Original file line number Diff line number Diff line change
@@ -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 <km_u16.h>
#include <typeinfo>
#include <cstring>
#include <stdarg.h>


#define CERR_None 0x00000000
#define CERR_CannotAllocateMemory 0x00008004
Expand Down Expand Up @@ -582,3 +586,28 @@ FILE* Open_File(const KMX_CHAR* filename, const KMX_CHAR* mode) {
return fopen(cpath.c_str(), cmode.c_str());
Comment thread
ermshiperete marked this conversation as resolved.
Outdated
#endif
};

#define _countof(a) (sizeof(a) / sizeof(*(a)))

/**
* @brief print (error) messages
* @param fmt text to print
*/
void KMX_LogError(const wchar_t* fmt, ...) {
Comment thread
ermshiperete marked this conversation as resolved.
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);
Comment thread
Markus-SWAG marked this conversation as resolved.
}

11 changes: 10 additions & 1 deletion linux/mcompile/keymap/mc_kmxfile.h
Original file line number Diff line number Diff line change
@@ -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 <kmx_file.h>
#include "mcompile.h"
#include <cstdio>

#ifndef _KMXFILE_H
#define _KMXFILE_H
Expand Down Expand Up @@ -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*/
108 changes: 4 additions & 104 deletions linux/mcompile/keymap/mcompile.cpp
Original file line number Diff line number Diff line change
@@ -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
*
Expand All @@ -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_DeadkeyMapping>* KMX_FDeadkeys, KMX_BOOL bDeadkeyConversion); // I4353 // I4327

Expand All @@ -25,85 +25,6 @@ int KMX_GetDeadkeys(vec_dword_2D& dk_Table, KMX_WORD deadkey, std::vector<KMX_WO

std::vector<KMX_DeadkeyMapping> 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};

Expand Down Expand Up @@ -555,24 +476,3 @@ int KMX_GetDeadkeys(vec_dword_2D& dk_Table, KMX_WORD deadkey, std::vector<KMX_WO
return dk_vec.size();
}

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