mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-10 10:55:33 +00:00
chore(web): Merge branch 'master' into chore/web/update-feature-esmodule-for-configure-fix
This commit is contained in:
commit
b85087f392
125 changed files with 3002 additions and 1569 deletions
48
HISTORY.md
48
HISTORY.md
|
|
@ -1,5 +1,53 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 17.0.74 alpha 2023-03-22
|
||||
|
||||
* chore(windows): update sentry-native to 0.6.0 (#8464)
|
||||
* chore(linux): Prevent building on s390x (#8477)
|
||||
* fix(linux): Display error message for corrupt .kmp file (#8479)
|
||||
|
||||
## 17.0.73 alpha 2023-03-21
|
||||
|
||||
* refactor(android/engine): Consolidate insertText (#8438)
|
||||
* chore(common): prevent multiple npm ci runs in child scripts (#8484)
|
||||
|
||||
## 17.0.72 alpha 2023-03-20
|
||||
|
||||
* chore(common): die early when --ci and --debug passed to test.sh (#8465)
|
||||
* chore(common): Update crowdin strings for Kibaku (#8447)
|
||||
|
||||
## 17.0.71 alpha 2023-03-18
|
||||
|
||||
* fix(core): ldml update fr-azerty for VKEY mapping (#8434)
|
||||
|
||||
## 17.0.70 alpha 2023-03-17
|
||||
|
||||
* chore(android): Document builder script steps (#8449)
|
||||
|
||||
## 17.0.69 alpha 2023-03-16
|
||||
|
||||
* chore(web): Cleanup build echo (#8446)
|
||||
* fix(developer): lm compiler handle missing line no in errors (#8444)
|
||||
* fix(android/app): Temporarily disable Keyman browser (#8430)
|
||||
* fix(android/engine): Add builder output for configure (#8442)
|
||||
* chore(linux): Update debian changelog (#8452)
|
||||
|
||||
## 17.0.68 alpha 2023-03-15
|
||||
|
||||
* chore(common): add common test build configurations (#8431)
|
||||
* fix(common): fix broken common/web/types cases (#8426)
|
||||
* fix(developer/compilers): locks esbuild target detection for kmc building (#8437)
|
||||
* (#8412)
|
||||
* (#8436)
|
||||
* chore(linux): Use dependency on core in ibus-keyman/build.sh (#8423)
|
||||
|
||||
## 17.0.67 alpha 2023-03-14
|
||||
|
||||
* (developer):Update copyright period in License.rtf (#8425)
|
||||
* chore(common): support shorthand for build.sh (#8415)
|
||||
* chore(common): build script performance improvements (#8416)
|
||||
* chore(common): TS updates to non-sync'd packages, feature-esmodule merge conflict prevention (#8429)
|
||||
|
||||
## 17.0.66 alpha 2023-03-13
|
||||
|
||||
* refactor(android): Use builder scripts (#7407)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
17.0.67
|
||||
17.0.75
|
||||
|
|
@ -435,9 +435,10 @@ public class MainActivity extends BaseActivity implements OnKeyboardEventListene
|
|||
case R.id.action_share:
|
||||
showShareDialog();
|
||||
return true;
|
||||
/* Disable Web Browser to investigate Google sign-in
|
||||
case R.id.action_web:
|
||||
showWebBrowser();
|
||||
return true;
|
||||
return true;*/
|
||||
case R.id.action_text_size:
|
||||
showTextSizeDialog();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@
|
|||
app:showAsAction="always"
|
||||
android:title="@string/action_share"
|
||||
android:icon="@drawable/ic_light_action_share" />
|
||||
|
||||
<item
|
||||
|
||||
<!-- Disable Web Browser to investigate Google sign-in -->
|
||||
<!--item
|
||||
android:id="@+id/action_web"
|
||||
app:showAsAction="always"
|
||||
android:title="@string/action_web"
|
||||
android:icon="@drawable/ic_light_action_web" />
|
||||
android:icon="@drawable/ic_light_action_web" /> -->
|
||||
|
||||
<item
|
||||
android:id="@+id/action_text_size"
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@
|
|||
app:showAsAction="always"
|
||||
android:title="@string/action_share"
|
||||
android:icon="@drawable/ic_light_action_share" />
|
||||
|
||||
<item
|
||||
|
||||
<!-- Disable Web Browser to investigate Google sign-in -->
|
||||
<!--item
|
||||
android:id="@+id/action_web"
|
||||
app:showAsAction="always"
|
||||
android:title="@string/action_web"
|
||||
android:icon="@drawable/ic_light_action_web" />
|
||||
android:icon="@drawable/ic_light_action_web" /-->
|
||||
|
||||
<item
|
||||
android:id="@+id/action_text_size"
|
||||
|
|
|
|||
|
|
@ -8,12 +8,13 @@
|
|||
app:showAsAction="always"
|
||||
android:title="@string/action_share"
|
||||
android:icon="@drawable/ic_light_action_share" />
|
||||
|
||||
<item
|
||||
|
||||
<!-- Disable Web Browser to investigate Google sign-in -->
|
||||
<!--item
|
||||
android:id="@+id/action_web"
|
||||
app:showAsAction="always"
|
||||
android:title="@string/action_web"
|
||||
android:icon="@drawable/ic_light_action_web" />
|
||||
android:icon="@drawable/ic_light_action_web" /> -->
|
||||
|
||||
<!-- Set showAsAction="never" for overflow -->
|
||||
<item
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- app_name removed in 16.0 beta. Set in build.gradle -->
|
||||
<!-- Context: Menu Action -->
|
||||
<string name="action_share" comment="Menu action to send text content to another app">Ndǝɦǝnta</string>
|
||||
<!-- Context: Menu Action -->
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
<!-- Context: Textfield prompt (… is the ellipsis character "...") -->
|
||||
<string name="textview_hint" comment="Prompt to start typing">Badicini taiping akwani…</string>
|
||||
<!-- Context: Splash screen (version/copyright info currently disabled -->
|
||||
<string name="splash_blank" translatable="false" comment="Blank text while version info on splash page is disabled"></string>
|
||||
<!-- Context: Splash screen (version/copyright info currently disabled -->
|
||||
<!-- Context: Text Size dialog -->
|
||||
<string name="text_size" comment="Current text size (number)">Ngakurǝr Text: %1$d</string>
|
||||
|
|
@ -82,7 +84,7 @@
|
|||
<!-- Context: Keyman Settings menu / Spacebar caption hint -->
|
||||
<string name="spacebar_caption_hint_blank" comment="Spacebar caption hint - blank">Ta gǝta tsanti kapshion ata spesba</string>
|
||||
<!-- Context: Keyman Settings menu / Haptic feedback -->
|
||||
<string name="haptic_feedback" comment="haptic feedback on key press">Vibrate when typing</string>
|
||||
<string name="haptic_feedback" comment="haptic feedback on key press">Ata kǝngǝri ko tuwa ma gata kǝra ʻya taiping</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
<string name="show_banner" comment="text suggestions banner">Tsanti banner kǝlara saka</string>
|
||||
<!-- Context: Keyman Settings menu -->
|
||||
|
|
@ -154,7 +156,7 @@
|
|||
<!-- Context: KMP Package strings -->
|
||||
<string name="invalid_metadata" comment="kmp.json metadata file is invalid or missing in package">Invalid/Missing metadata in package</string>
|
||||
<!-- Context: KMP Package strings -->
|
||||
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman"> Keyboard requires a newer version of Keyman</string>
|
||||
<string name="minimum_keyboard_version_not_supported" comment="Notification when keyboard has functionality not supported by current Keyman"> Kibod ǝni ada kǝthlǝr aka jiliyir Keyman nam bǝlin. </string>
|
||||
<!-- Context: anywhere -->
|
||||
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Unable to launch web browser</string>
|
||||
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Yar ta mǝzi ka mburnta brauza wɛ</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -67,12 +67,12 @@ import io.sentry.SentryLevel;
|
|||
final class KMKeyboard extends WebView {
|
||||
private static final String TAG = "KMKeyboard";
|
||||
private final Context context;
|
||||
private KeyboardType keyboardType = KeyboardType.KEYBOARD_TYPE_UNDEFINED;
|
||||
private String packageID;
|
||||
private String keyboardID;
|
||||
private String keyboardName;
|
||||
private String keyboardVersion;
|
||||
|
||||
protected KeyboardType keyboardType = KeyboardType.KEYBOARD_TYPE_UNDEFINED;
|
||||
protected ArrayList<String> javascriptAfterLoad = new ArrayList<String>();
|
||||
|
||||
private static String currentKeyboard = null;
|
||||
|
|
|
|||
|
|
@ -2,15 +2,27 @@ package com.keyman.engine;
|
|||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.Vibrator;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.webkit.JavascriptInterface;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import static android.content.Context.VIBRATOR_SERVICE;
|
||||
|
||||
import com.keyman.engine.KMManager.KeyboardType;
|
||||
import com.keyman.engine.util.CharSequenceUtil;
|
||||
import com.keyman.engine.util.KMLog;
|
||||
|
||||
public abstract class KMKeyboardJSHandler {
|
||||
private Context context;
|
||||
private KMKeyboard k = null;
|
||||
|
|
@ -64,6 +76,126 @@ public abstract class KMKeyboardJSHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the selected string <i>s</i>
|
||||
* @param dn Number of pre-caret code points (UTF+8 characters) to delete
|
||||
* @param s Text to insert
|
||||
* @param dr Number of post-caret code points to delete.
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public void insertText(final int dn, final String s, final int dr, final boolean executingHardwareKeystroke) {
|
||||
Handler mainLoop = new Handler(Looper.getMainLooper());
|
||||
mainLoop.post(new Runnable() {
|
||||
public void run() {
|
||||
if (k == null) {
|
||||
KMLog.LogError(TAG, "insertText failed: Keyboard is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (k.subKeysWindow != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP &&
|
||||
(KMTextView.activeView == null || KMTextView.activeView.getClass() != KMTextView.class)) {
|
||||
if (KMTextView.activeView == null && KMManager.isDebugMode()) {
|
||||
Log.w(TAG, "insertText failed: activeView is null");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
InputConnection ic = (k.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) ?
|
||||
KMTextView.activeView.onCreateInputConnection(new EditorInfo()) :
|
||||
KMManager.getInputMethodService().getCurrentInputConnection();
|
||||
if (ic == null) {
|
||||
if (KMManager.isDebugMode()) {
|
||||
Log.w(TAG, "insertText failed: InputConnection is null");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ic.beginBatchEdit();
|
||||
|
||||
int deleteLeft = dn;
|
||||
|
||||
// Delete any existing selected text.
|
||||
ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0);
|
||||
if (icText != null) { // This can be null if the input connection becomes invalid.
|
||||
int start = icText.startOffset + icText.selectionStart;
|
||||
int end = icText.startOffset + icText.selectionEnd;
|
||||
if (end < start) {
|
||||
// Swap start/end for backward selection
|
||||
int temp = start;
|
||||
start = end;
|
||||
end = temp;
|
||||
}
|
||||
if (end > start) {
|
||||
if (s.length() == 0) {
|
||||
ic.setSelection(start, start);
|
||||
ic.deleteSurroundingText(0, end - start);
|
||||
ic.endBatchEdit();
|
||||
return;
|
||||
} else {
|
||||
if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
KMManager.SystemKeyboardShouldIgnoreSelectionChange = true;
|
||||
}
|
||||
ic.setSelection(start, start);
|
||||
ic.deleteSurroundingText(0, end - start);
|
||||
}
|
||||
|
||||
// KeymanWeb tells us how to delete the selection, but we don't
|
||||
// want to do that twice
|
||||
deleteLeft = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.length() > 0 && s.charAt(0) == '\n') {
|
||||
keyDownUp(KeyEvent.KEYCODE_ENTER);
|
||||
ic.endBatchEdit();
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform left-deletions
|
||||
if (deleteLeft > 0) {
|
||||
performLeftDeletions(ic, deleteLeft);
|
||||
}
|
||||
|
||||
// Perform right-deletions
|
||||
for (int i = 0; i < dr; i++) {
|
||||
CharSequence chars = ic.getTextAfterCursor(1, 0);
|
||||
if (chars != null && chars.length() > 0) {
|
||||
char c = chars.charAt(0);
|
||||
if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
KMManager.SystemKeyboardShouldIgnoreSelectionChange = true;
|
||||
}
|
||||
if (Character.isHighSurrogate(c)) {
|
||||
ic.deleteSurroundingText(0, 2);
|
||||
} else {
|
||||
ic.deleteSurroundingText(0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.length() > 0) {
|
||||
if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
KMManager.SystemKeyboardShouldIgnoreSelectionChange = true;
|
||||
}
|
||||
// Commit the string s. Use newCursorPosition 1 so cursor will end up after the string.
|
||||
ic.commitText(s, 1);
|
||||
}
|
||||
|
||||
k.dismissHelpBubble();
|
||||
k.setShouldShowHelpBubble(false);
|
||||
|
||||
ic.endBatchEdit();
|
||||
ViewGroup parent = (ViewGroup) k.getParent();
|
||||
if (parent != null && KMManager.getHapticFeedback() && !executingHardwareKeystroke) {
|
||||
parent.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Store the current keyboard chirality status from KMW in the Keyboard
|
||||
@JavascriptInterface
|
||||
public void setIsChiral(boolean isChiral) {
|
||||
|
|
@ -73,14 +205,90 @@ public abstract class KMKeyboardJSHandler {
|
|||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public abstract boolean dispatchKey(final int code, final int eventModifiers);
|
||||
public abstract boolean dispatchKey(final int code, final int eventModifiers);
|
||||
|
||||
private void keyDownUp(int keyEventCode) {
|
||||
if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_INAPP) {
|
||||
KMTextView textView = (KMTextView)KMTextView.activeView;
|
||||
textView.keyDownUp(KeyEvent.KEYCODE_ENTER);
|
||||
} else if (k.keyboardType == KeyboardType.KEYBOARD_TYPE_SYSTEM) {
|
||||
KMManager.getInputMethodService().getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
|
||||
KMManager.getInputMethodService().getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Chromium up until version M81 had a bug where deleteSurroundingText deletes an entire
|
||||
// grapheme cluster instead of one code-point. See Chromium issue #1024738
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1024738
|
||||
//
|
||||
// We'll retrieve up to (dn*2+16) characters before the cursor to collect enough characters
|
||||
// for surrogate pairs + a long grapheme cluster.
|
||||
// This buffer will be used to put back characters as-needed
|
||||
*/
|
||||
private static void performLeftDeletions(InputConnection ic, int dn) {
|
||||
int originalBufferLength = dn*2 + 16; // characters
|
||||
CharSequence charsBackup = getCharacterSequence(ic, originalBufferLength);
|
||||
|
||||
int lastIndex = charsBackup.length()-1;
|
||||
|
||||
// Exit if there's no context to delete
|
||||
if (lastIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Count the number of characters which are surrogate pairs
|
||||
int numPairs = CharSequenceUtil.countSurrogatePairs(charsBackup, dn);
|
||||
|
||||
// Chop dn+numPairs code points from the end of charsBackup
|
||||
// subSequence indices are start(inclusive) to end(exclusive)
|
||||
CharSequence expectedChars = charsBackup.subSequence(0, charsBackup.length() - (dn + numPairs));
|
||||
ic.deleteSurroundingText(dn + numPairs, 0);
|
||||
CharSequence newContext = getCharacterSequence(ic, originalBufferLength - 2*dn);
|
||||
|
||||
CharSequence charsToRestore = CharSequenceUtil.restoreChars(expectedChars, newContext);
|
||||
if (charsToRestore.length() > 0) {
|
||||
// Restore expectedChars that Chromium deleted.
|
||||
// Use newCusorPosition 1 so cursor will be after the inserted string
|
||||
ic.commitText(charsToRestore, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the selected string <i>s</i>
|
||||
* @param dn Number of pre-caret code points (UTF+8 characters) to delete
|
||||
* @param s Text to insert
|
||||
* @param dr Number of post-caret code points to delete.
|
||||
* Get a character sequence from the InputConnection.
|
||||
* Sometimes the WebView can split a surrogate pair at either end,
|
||||
* so chop that and update the cursor
|
||||
* @param ic - the InputConnection
|
||||
* @param length - number of characters to get
|
||||
* @return CharSequence
|
||||
*/
|
||||
@JavascriptInterface
|
||||
public abstract void insertText(final int dn, final String s, final int dr, final boolean executingHardwareKeystroke);
|
||||
private static CharSequence getCharacterSequence(InputConnection ic, int length) {
|
||||
if (ic == null || length <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
CharSequence sequence = ic.getTextBeforeCursor(length, 0);
|
||||
if (sequence == null || sequence.length() <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Move the cursor back if there's a split surrogate pair
|
||||
if (Character.isHighSurrogate(sequence.charAt(sequence.length()-1))) {
|
||||
ic.commitText("", -1);
|
||||
sequence = ic.getTextBeforeCursor(length, 0);
|
||||
}
|
||||
|
||||
if (sequence == null || sequence.length() <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (Character.isLowSurrogate(sequence.charAt(0))) {
|
||||
// Adjust if the first char is also a split surrogate pair
|
||||
// subSequence indices are start(inclusive) to end(exclusive)
|
||||
sequence = sequence.subSequence(1, sequence.length());
|
||||
}
|
||||
|
||||
return sequence;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2264,124 +2264,6 @@ public final class KMManager {
|
|||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// This annotation is required in Jelly Bean and later:
|
||||
@JavascriptInterface
|
||||
public void insertText(final int dn, final String s, final int dr, final boolean executingHardwareKeystroke) {
|
||||
if(dr != 0) {
|
||||
Log.d(TAG, "Right deletions requested but are not presently supported by the in-app keyboard.");
|
||||
}
|
||||
|
||||
Handler mainLoop = new Handler(Looper.getMainLooper());
|
||||
mainLoop.post(new Runnable() {
|
||||
public void run() {
|
||||
if (InAppKeyboard == null) {
|
||||
KMLog.LogError(TAG, "insertText failed: InAppKeyboard is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (InAppKeyboard.subKeysWindow != null || KMTextView.activeView == null || KMTextView.activeView.getClass() != KMTextView.class) {
|
||||
if ((KMTextView.activeView == null) && isDebugMode()) {
|
||||
Log.w("IAK: JS Handler", "insertText failed: activeView is null");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
InAppKeyboard.dismissHelpBubble();
|
||||
InAppKeyboard.setShouldShowHelpBubble(false);
|
||||
|
||||
KMTextView textView = (KMTextView) KMTextView.activeView;
|
||||
textView.beginBatchEdit();
|
||||
|
||||
int start = textView.getSelectionStart();
|
||||
int end = textView.getSelectionEnd();
|
||||
// Workaround for Android TextView bug where end < start
|
||||
// Reference: https://issuetracker.google.com/issues/36911048
|
||||
if (end < start) {
|
||||
Log.d(TAG, "Swapping TextView selection end:" + end + " and start:" + start);
|
||||
int temp = end;
|
||||
end = start;
|
||||
start = temp;
|
||||
}
|
||||
|
||||
int deleteLeft = dn;
|
||||
|
||||
if(start != end && dn == 1 && s.length() == 0) {
|
||||
/* Handle backspace with a selection: just delete selection */
|
||||
deleteLeft = 0;
|
||||
}
|
||||
|
||||
if (deleteLeft <= 0) {
|
||||
if (start == end) {
|
||||
if (s.length() > 0 && s.charAt(0) == '\n') {
|
||||
textView.keyDownUp(KeyEvent.KEYCODE_ENTER);
|
||||
} else if (s.length() > 0) {
|
||||
// *** TO DO: Try to find a solution to the bug on API < 17, insert overwrites on next line
|
||||
InAppKeyboardShouldIgnoreTextChange = true;
|
||||
InAppKeyboardShouldIgnoreSelectionChange = true;
|
||||
textView.getText().insert(start, s);
|
||||
} else {
|
||||
textView.getText().delete(start, end);
|
||||
}
|
||||
} else {
|
||||
if (s.length() > 0 && s.charAt(0) == '\n') {
|
||||
InAppKeyboardShouldIgnoreTextChange = true;
|
||||
InAppKeyboardShouldIgnoreSelectionChange = true;
|
||||
textView.getText().replace(start, end, "");
|
||||
textView.keyDownUp(KeyEvent.KEYCODE_ENTER);
|
||||
} else {
|
||||
if (s.length() == 0) {
|
||||
textView.getText().delete(start, end);
|
||||
} else {
|
||||
InAppKeyboardShouldIgnoreTextChange = true;
|
||||
InAppKeyboardShouldIgnoreSelectionChange = true;
|
||||
textView.getText().replace(start, end, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(start != end) {
|
||||
// Delete the selection
|
||||
InAppKeyboardShouldIgnoreTextChange = true;
|
||||
InAppKeyboardShouldIgnoreSelectionChange = true;
|
||||
textView.getText().delete(start, end);
|
||||
textView.setSelection(start);
|
||||
end = start;
|
||||
deleteLeft = 0;
|
||||
}
|
||||
for (int i = 0; i < deleteLeft; i++) {
|
||||
CharSequence chars = textView.getText().subSequence(0, start);
|
||||
if (chars != null && chars.length() > 0) {
|
||||
char c = chars.charAt(start - 1);
|
||||
InAppKeyboardShouldIgnoreTextChange = true;
|
||||
InAppKeyboardShouldIgnoreSelectionChange = true;
|
||||
if (Character.isLowSurrogate(c)) {
|
||||
textView.getText().delete(start - 2, end);
|
||||
} else {
|
||||
textView.getText().delete(start - 1, end);
|
||||
}
|
||||
|
||||
start = textView.getSelectionStart();
|
||||
end = textView.getSelectionEnd();
|
||||
}
|
||||
}
|
||||
|
||||
if (s.length() > 0) {
|
||||
InAppKeyboardShouldIgnoreTextChange = true;
|
||||
InAppKeyboardShouldIgnoreSelectionChange = true;
|
||||
textView.getText().insert(start, s);
|
||||
}
|
||||
}
|
||||
|
||||
// Collapse the selection
|
||||
textView.setSelection(start + s.length());
|
||||
textView.endBatchEdit();
|
||||
if (mayHaveHapticFeedback && !executingHardwareKeystroke) {
|
||||
textView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static final class KMSystemKeyboardJSHandler extends KMKeyboardJSHandler {
|
||||
|
|
@ -2428,185 +2310,5 @@ public final class KMManager {
|
|||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
// This annotation is required in Jelly Bean and later:
|
||||
@JavascriptInterface
|
||||
public void insertText(final int dn, final String s, final int dr, final boolean executingHardwareKeystroke) {
|
||||
// TODO: Unify in-app and system insertText
|
||||
Handler mainLoop = new Handler(Looper.getMainLooper());
|
||||
mainLoop.post(new Runnable() {
|
||||
public void run() {
|
||||
if (SystemKeyboard == null) {
|
||||
KMLog.LogError(TAG, "insertText failed: SystemKeyboard is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SystemKeyboard.subKeysWindow != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
InputConnection ic = IMService.getCurrentInputConnection();
|
||||
if (ic == null) {
|
||||
if (isDebugMode()) {
|
||||
Log.w(HANDLER_TAG, "insertText failed: InputConnection is null");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ic.beginBatchEdit();
|
||||
|
||||
int deleteLeft = dn;
|
||||
|
||||
// Delete any existing selected text.
|
||||
ExtractedText icText = ic.getExtractedText(new ExtractedTextRequest(), 0);
|
||||
if (icText != null) { // This can be null if the input connection becomes invalid.
|
||||
int start = icText.startOffset + icText.selectionStart;
|
||||
int end = icText.startOffset + icText.selectionEnd;
|
||||
if (end < start) {
|
||||
// Swap start/end for backward selection
|
||||
int temp = start;
|
||||
start = end;
|
||||
end = temp;
|
||||
}
|
||||
if (end > start) {
|
||||
if (s.length() == 0) {
|
||||
ic.setSelection(start, start);
|
||||
ic.deleteSurroundingText(0, end - start);
|
||||
ic.endBatchEdit();
|
||||
return;
|
||||
} else {
|
||||
SystemKeyboardShouldIgnoreSelectionChange = true;
|
||||
ic.setSelection(start, start);
|
||||
ic.deleteSurroundingText(0, end - start);
|
||||
}
|
||||
|
||||
// KeymanWeb tells us how to delete the selection, but we don't
|
||||
// want to do that twice
|
||||
deleteLeft = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (s.length() > 0 && s.charAt(0) == '\n') {
|
||||
keyDownUp(KeyEvent.KEYCODE_ENTER);
|
||||
ic.endBatchEdit();
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform left-deletions
|
||||
if (deleteLeft > 0) {
|
||||
performLeftDeletions(ic, deleteLeft);
|
||||
}
|
||||
|
||||
// Perform right-deletions
|
||||
for (int i = 0; i < dr; i++) {
|
||||
CharSequence chars = ic.getTextAfterCursor(1, 0);
|
||||
if (chars != null && chars.length() > 0) {
|
||||
char c = chars.charAt(0);
|
||||
SystemKeyboardShouldIgnoreSelectionChange = true;
|
||||
if (Character.isHighSurrogate(c)) {
|
||||
ic.deleteSurroundingText(0, 2);
|
||||
} else {
|
||||
ic.deleteSurroundingText(0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.length() > 0) {
|
||||
SystemKeyboardShouldIgnoreSelectionChange = true;
|
||||
|
||||
// Commit the string s. Use newCursorPosition 1 so cursor will end up after the string.
|
||||
ic.commitText(s, 1);
|
||||
}
|
||||
|
||||
SystemKeyboard.dismissHelpBubble();
|
||||
SystemKeyboard.setShouldShowHelpBubble(false);
|
||||
|
||||
ic.endBatchEdit();
|
||||
ViewGroup parent = (ViewGroup) SystemKeyboard.getParent();
|
||||
if (parent != null && mayHaveHapticFeedback && !executingHardwareKeystroke) {
|
||||
parent.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void keyDownUp(int keyEventCode) {
|
||||
IMService.getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode));
|
||||
IMService.getCurrentInputConnection().sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyEventCode));
|
||||
}
|
||||
|
||||
/*
|
||||
// Chromium up until version M81 had a bug where deleteSurroundingText deletes an entire
|
||||
// grapheme cluster instead of one code-point. See Chromium issue #1024738
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1024738
|
||||
//
|
||||
// We'll retrieve up to (dn*2+16) characters before the cursor to collect enough characters
|
||||
// for surrogate pairs + a long grapheme cluster.
|
||||
// This buffer will be used to put back characters as-needed
|
||||
*/
|
||||
private static void performLeftDeletions(InputConnection ic, int dn) {
|
||||
int originalBufferLength = dn*2 + 16; // characters
|
||||
CharSequence charsBackup = getCharacterSequence(ic, originalBufferLength);
|
||||
|
||||
int lastIndex = charsBackup.length()-1;
|
||||
|
||||
// Exit if there's no context to delete
|
||||
if (lastIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Count the number of characters which are surrogate pairs
|
||||
int numPairs = CharSequenceUtil.countSurrogatePairs(charsBackup, dn);
|
||||
|
||||
// Chop dn+numPairs code points from the end of charsBackup
|
||||
// subSequence indices are start(inclusive) to end(exclusive)
|
||||
CharSequence expectedChars = charsBackup.subSequence(0, charsBackup.length() - (dn + numPairs));
|
||||
ic.deleteSurroundingText(dn + numPairs, 0);
|
||||
CharSequence newContext = getCharacterSequence(ic, originalBufferLength - 2*dn);
|
||||
|
||||
CharSequence charsToRestore = CharSequenceUtil.restoreChars(expectedChars, newContext);
|
||||
if (charsToRestore.length() > 0) {
|
||||
// Restore expectedChars that Chromium deleted.
|
||||
// Use newCusorPosition 1 so cursor will be after the inserted string
|
||||
ic.commitText(charsToRestore, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a character sequence from the InputConnection.
|
||||
* Sometimes the WebView can split a surrogate pair at either end,
|
||||
* so chop that and update the cursor
|
||||
* @param ic - the InputConnection
|
||||
* @param length - number of characters to get
|
||||
* @return CharSequence
|
||||
*/
|
||||
private static CharSequence getCharacterSequence(InputConnection ic, int length) {
|
||||
if (ic == null || length <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
CharSequence sequence = ic.getTextBeforeCursor(length, 0);
|
||||
if (sequence == null || sequence.length() <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Move the cursor back if there's a split surrogate pair
|
||||
if (Character.isHighSurrogate(sequence.charAt(sequence.length()-1))) {
|
||||
ic.commitText("", -1);
|
||||
sequence = ic.getTextBeforeCursor(length, 0);
|
||||
}
|
||||
|
||||
if (sequence == null || sequence.length() <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (Character.isLowSurrogate(sequence.charAt(0))) {
|
||||
// Adjust if the first char is also a split surrogate pair
|
||||
// subSequence indices are start(inclusive) to end(exclusive)
|
||||
sequence = sequence.subSequence(1, sequence.length());
|
||||
}
|
||||
|
||||
return sequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
</plurals>
|
||||
<!-- Context: Title for list -->
|
||||
<plurals name="title_other_input_methods">
|
||||
<item quantity="one">Other Input Method</item>
|
||||
<item quantity="other">Other Input Methods</item>
|
||||
<item quantity="one">Whalǝr ʻYa ko pu Suyere Damwa</item>
|
||||
<item quantity="other">Whalǝr ʻYa ko Pu Suyere</item>
|
||||
</plurals>
|
||||
<!-- Context: Title for list -->
|
||||
<string name="title_add_keyboard" comment="Add a new keyboard">Dzakǝr Kibod Ɓǝlin</string>
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
<!-- Context: Button label -->
|
||||
<string name="label_update" comment="Dialog button to update a resource">Update</string>
|
||||
<!-- Context: No internet for updates -->
|
||||
<string name="no_internet_connection" comment="Internet connection missing">No internet connection</string>
|
||||
<string name="no_internet_connection" comment="Internet connection missing">Layǝr intanet adiwɛ </string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
<string name="cannot_connect" comment="Error message when network connection fails">Adi ata mǝzi ɓui ka lukwa Keyman server wɛ!</string>
|
||||
<!-- Context: Keyboard Updates -->
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
<string name="fatal_keyboard_error_short" comment="Error in keyboard (keyboard ID::packageID for language)."> Su kura kal kalakwa kibod %1$s:%2$s nǝr %3$s nya.</string>
|
||||
<!-- Context: Query for associated dictionary -->
|
||||
<string name="query_associated_model" comment="Check if there's an available dictionary to download">Ata kǝra chakǝnta akuyaka ɓui suyere tara tsukwar bwar dictionary kadǝbar ka ndǝ download ko sǝgǝl akǝr</string>
|
||||
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Cannot connect to Keyman server to check for associated dictionary to download</string>
|
||||
<string name="cannot_query_associated_model" comment="Cannot reach server to check if there's an available dictionary to download"> Gadiyata mǝzi bui sava nǝr Keyman wɛ akuya ka gǝ bui ka sǝgǝl aka jiliyir dikshinari namtǝ gada bara. </string>
|
||||
<!-- Context: Model Updates -->
|
||||
<string name="confirm_download_model" comment="Confirmation to download a dictionary update">Gada bara download vershon nam ɓǝlin nǝr kibod ǝni ya?</string>
|
||||
<!-- Context: No associated dictionary found -->
|
||||
|
|
@ -128,11 +128,11 @@
|
|||
<!-- Context: Model Info -->
|
||||
<string name="confirm_delete_model" comment="Confirmation to delete a dictionary">Gada bara kǝlba ka ɗalba dictionary ǝni ya?</string>
|
||||
<!-- Context: Model Deleted -->
|
||||
<string name="model_deleted" comment="Notification when a dictionary is deleted">Dictionary deleted</string>
|
||||
<string name="model_deleted" comment="Notification when a dictionary is deleted">Ndɛ kǝlba ka ɗalba dikshinari.</string>
|
||||
<!-- Context: Language Settings Keyboard install strings -->
|
||||
<string name="keyboard_install_toast" comment="Notification when a keyboard is installed">%1$s ndǝ kurta vu kibod</string>
|
||||
<!-- Context: Language Settings Keyboard uninstall strings -->
|
||||
<string name="keyboard_deleted_toast" comment="Notification when a keyboard is deleted">Keyboard deleted</string>
|
||||
<string name="keyboard_deleted_toast" comment="Notification when a keyboard is deleted">Ndǝ kǝlba ka ɗalba Kibod </string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<string name="enable_corrections" comment="Enable corrections from the suggestion banner">Mǝrti ka dza mǝzi ʻyati ka dzǝ dzukti suwar su wato correctionyere</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
|
|
@ -140,12 +140,12 @@
|
|||
<!-- Context: Language Settings menu strings -->
|
||||
<string name="model_label">Dictionary</string>
|
||||
<plurals name="model_count">
|
||||
<item quantity="one">Dictionary</item>
|
||||
<item quantity="other">Dictionaries</item>
|
||||
<item quantity="one">Dikshinari</item>
|
||||
<item quantity="other">Dikshinariyere </item>
|
||||
</plurals>
|
||||
<!-- Context: Language Settings menu substring - Check for available dictionary -->
|
||||
<string name="check_available_model" comment="Check for available dictionary">Baraba ka zǝndi dictionary nam adai</string>
|
||||
<string name="check_model_online" comment="Check for dictionaries online">Check for dictionaries online</string>
|
||||
<string name="check_model_online" comment="Check for dictionaries online">Wulba ka gǝ bui dikshinariyere akwa layir shambar</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
<string name="model_info_header" comment="Dictionary name">Dictionary: %1$s</string>
|
||||
<!-- Context: Language Settings menu strings -->
|
||||
|
|
@ -168,5 +168,5 @@
|
|||
<!-- Context: Other strings -->
|
||||
<string name="help_bubble_text">Tsukwar vi ǝni akuya ka gǝ pali kibod</string>
|
||||
<!-- Context: anywhere -->
|
||||
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Unable to launch web browser</string>
|
||||
<string name="unable_to_open_browser" comment="Notification when a browser activity cannot be launched">Yar ta mǝzi ka mburnta brauza wɛ</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ if builder_is_debug_build; then
|
|||
fi
|
||||
|
||||
builder_describe_outputs \
|
||||
configure:engine /android/KMEA/app/src/main/assets/keymanandroid.js \
|
||||
build:engine /android/KMEA/app/build/outputs/aar/${CONFIG}/keyman-engine.aar
|
||||
|
||||
#### Build
|
||||
|
|
|
|||
|
|
@ -25,9 +25,7 @@ Keyman for Android uses [Sentry](https://sentry.io) for crash reporting at a ser
|
|||
* Compile KMEA (and its KMW dependency)
|
||||
* Download default keyboard and dictionary resources as needed
|
||||
* Compile KMAPro
|
||||
* Note: to force an update to the latest keyboard and dictionary packages, use the `--download-resources` flag.
|
||||
|
||||
3. The APK will be found in **keyman/android/KMAPro/kMAPro/build/outputs/apk/debug/kMAPro-debug.apk**
|
||||
3. The APK will be found in **keyman/android/KMAPro/kMAPro/build/outputs/apk/debug/keyman-${version}.apk** where `${version}` is the current version number.
|
||||
|
||||
### Compiling From Android Studio
|
||||
1. Ensure that [Keyman Engine for Android](#how-to-build-keyman-engine-for-android) is built.
|
||||
|
|
@ -60,10 +58,11 @@ Keyman for Android uses [Sentry](https://sentry.io) for crash reporting at a ser
|
|||
|
||||
### Compiling the app's offline help
|
||||
Keyman for Android help is maintained in the Markdown files in android/help/.
|
||||
The script `build-help.sh` uses the `pandoc` tool to convert the Markdown files into html.
|
||||
The script `/resources/build/build-help.inc.sh` uses the `pandoc` tool to convert the Markdown files into html.
|
||||
|
||||
```bash
|
||||
./build-help.sh htm
|
||||
# Convert markdown to html for offline help
|
||||
build_help_html android KMAPro/kMAPro/src/main/assets/info
|
||||
```
|
||||
|
||||
This script is automatically called when Keyman for Android is built.
|
||||
|
|
@ -80,7 +79,7 @@ Both sample apps include a default Tamil keyboard and sample dictionary.
|
|||
Building these projects follow the same steps as KMAPro:
|
||||
|
||||
1. cd to the desired KMSample directory
|
||||
2. `./build.sh`
|
||||
2. `./build.sh configure build --debug`
|
||||
3. Open Android Studio to run the app
|
||||
|
||||
### Tests: KeyboardHarness
|
||||
|
|
@ -93,7 +92,7 @@ Building these projects follow the same steps as KMAPro:
|
|||
* Build the keyboardharness.kmp keyboard package
|
||||
3. Add the keyboard in *android/Tests/KeyboardHarness/app/src/main/java/com/keyman/android/tests/keyboardHarness/MainActivity.java*
|
||||
4. cd to android/Tests/KeyboardHarness/
|
||||
5. `./build.sh`
|
||||
5. `./build.sh configure build --debug`
|
||||
6. Open Android Studio to run the app
|
||||
|
||||
--------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ builder_describe "Build KMSample1 app for Android." \
|
|||
"clean" \
|
||||
"configure" \
|
||||
"build" \
|
||||
"test" \
|
||||
":app KMSample1" \
|
||||
"--ci Don't start the Gradle daemon. Use for CI"
|
||||
|
||||
|
|
@ -75,6 +76,10 @@ fi
|
|||
if builder_start_action build:app; then
|
||||
./gradlew clean $SAMPLE_FLAGS
|
||||
|
||||
|
||||
builder_finish_action success build:app
|
||||
fi
|
||||
|
||||
if builder_start_action test:app; then
|
||||
# TODO: define tests
|
||||
builder_finish_action success test:app
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ builder_describe "Build KMSample2 app for Android." \
|
|||
"clean" \
|
||||
"configure" \
|
||||
"build" \
|
||||
"test" \
|
||||
":app KMSample2" \
|
||||
"--ci Don't start the Gradle daemon. Use for CI"
|
||||
|
||||
|
|
@ -75,6 +76,10 @@ fi
|
|||
if builder_start_action build:app; then
|
||||
./gradlew clean $SAMPLE_FLAGS
|
||||
|
||||
|
||||
builder_finish_action success build:app
|
||||
fi
|
||||
|
||||
if builder_start_action test:app; then
|
||||
# TODO: define tests
|
||||
builder_finish_action success test:app
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ builder_describe "Build KeyboardHarness test app for Android." \
|
|||
"clean" \
|
||||
"configure" \
|
||||
"build" \
|
||||
"test" \
|
||||
":app KeyboardHarness" \
|
||||
"--ci Don't start the Gradle daemon. Use for CI"
|
||||
|
||||
|
|
@ -89,6 +90,6 @@ fi
|
|||
|
||||
if builder_start_action test:app; then
|
||||
echo "TEST_FLAGS $TEST_FLAGS"
|
||||
|
||||
# TODO: define tests
|
||||
builder_finish_action success test:app
|
||||
fi
|
||||
|
|
|
|||
30
common/linux/build.sh
Executable file
30
common/linux/build.sh
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Compiles and tests the common Linux modules
|
||||
#
|
||||
|
||||
# Exit on command failure and when using unset variables:
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
#
|
||||
# TODO: when we have linux-specific tests, add them here
|
||||
# as child modules
|
||||
#
|
||||
|
||||
builder_describe "Keyman common Linux modules" \
|
||||
clean \
|
||||
configure \
|
||||
build \
|
||||
test
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
builder_run_child_actions clean configure build test
|
||||
30
common/mac/build.sh
Executable file
30
common/mac/build.sh
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Compiles and tests the common mac modules
|
||||
#
|
||||
|
||||
# Exit on command failure and when using unset variables:
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
#
|
||||
# TODO: when we have mac-specific tests, add them here
|
||||
# as child modules
|
||||
#
|
||||
|
||||
builder_describe "Keyman common mac modules" \
|
||||
clean \
|
||||
configure \
|
||||
build \
|
||||
test
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
builder_run_child_actions clean configure build test
|
||||
|
|
@ -29,6 +29,10 @@ builder_describe "Runs all tests for the language-modeling / predictive-text lay
|
|||
|
||||
builder_parse "$@"
|
||||
|
||||
if builder_has_option --ci && builder_is_debug_build; then
|
||||
builder_die "Options --ci and --debug are incompatible."
|
||||
fi
|
||||
|
||||
if builder_start_action configure; then
|
||||
verify_npm_setup
|
||||
builder_finish_action success configure
|
||||
|
|
@ -126,10 +130,6 @@ if builder_start_action test:browser; then
|
|||
KARMA_FLAGS="$KARMA_FLAGS --reporters teamcity,BrowserStack"
|
||||
KARMA_CONFIG="CI.conf.cjs"
|
||||
KARMA_INFO_LEVEL="--log-level=debug"
|
||||
|
||||
if builder_is_debug_build; then
|
||||
echo "$(builder_term --ci) option set; ignoring $(builder_term --debug) option"
|
||||
fi
|
||||
else
|
||||
KARMA_CONFIG="manual.conf.cjs"
|
||||
if builder_is_debug_build; then
|
||||
|
|
|
|||
34
common/web/build.sh
Executable file
34
common/web/build.sh
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Compiles and tests the common web modules
|
||||
#
|
||||
|
||||
# Exit on command failure and when using unset variables:
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
#
|
||||
# TODO: future modules may include
|
||||
# :lm-message-types \
|
||||
# :sentry-manager \
|
||||
#
|
||||
|
||||
builder_describe "Keyman common web modules" \
|
||||
:keyman-version \
|
||||
:types \
|
||||
:utils \
|
||||
clean \
|
||||
configure \
|
||||
build \
|
||||
test
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
builder_run_child_actions clean configure build test
|
||||
|
|
@ -24,6 +24,7 @@ builder_describe "Build the include script for current Keyman version" \
|
|||
clean \
|
||||
build \
|
||||
publish \
|
||||
test \
|
||||
--dry-run
|
||||
|
||||
builder_describe_outputs \
|
||||
|
|
|
|||
|
|
@ -108,13 +108,9 @@ export interface LKFlick {
|
|||
|
||||
export interface LKLayers {
|
||||
/**
|
||||
* `hardware` or `touch`
|
||||
* `touch`, or hardware `us`, `iso`, `jis`, `abnt2`
|
||||
*/
|
||||
form?: string;
|
||||
/**
|
||||
* `us`, `iso`, `jis`, or `abnt2`
|
||||
*/
|
||||
hardware?: string;
|
||||
/**
|
||||
* Minimum width in millimeters
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -6,12 +6,16 @@ import { CompilerCallbacks, CompilerEvent } from '../../src/util/compiler-interf
|
|||
import { LDMLKeyboardXMLSourceFile } from '../../src/ldml-keyboard/ldml-keyboard-xml.js';
|
||||
import { LDMLKeyboardTestDataXMLSourceFile } from '../ldml-keyboard/ldml-keyboard-testdata-xml.js';
|
||||
|
||||
// TODO-LDML: this is largely a port from developer/src/kmc-keyboard/test/helpers/index.ts
|
||||
// This is related to developer/src/kmc-keyboard/test/helpers/index.ts but has a slightly different API surface
|
||||
// as this runs at a lower level than the compiler.
|
||||
|
||||
/**
|
||||
* A CompilerCallbacks implementation for testing
|
||||
*/
|
||||
class TestCompilerCallbacks implements CompilerCallbacks {
|
||||
loadKpjJsonSchema(): Buffer {
|
||||
throw new Error('loadKpjJsonSchema not implemented.'); // not needed for this test
|
||||
}
|
||||
loadLdmlKeyboardTestSchema(): Buffer {
|
||||
return loadLdmlKeyboardTestDataSchema();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ import 'mocha';
|
|||
import {assert} from 'chai';
|
||||
import { loadKpjJsonSchema, makePathToFixture } from '../helpers/index.js';
|
||||
import { KPJFileReader } from "../../src/kpj/kpj-file-reader.js";
|
||||
import { KeymanDeveloperProjectType } from '../../src/kpj/keyman-developer-project.js';
|
||||
import { KeymanDeveloperProjectFile10, KeymanDeveloperProjectType } from '../../src/kpj/keyman-developer-project.js';
|
||||
|
||||
describe('kpj-file-reader', function () {
|
||||
it('kpj-file-reader should read a valid file', function() {
|
||||
const path = makePathToFixture('kpj', 'khmer_angkor.kpj');
|
||||
const kpjPath = 'khmer_angkor.kpj';
|
||||
const path = makePathToFixture('kpj', kpjPath);
|
||||
const input = fs.readFileSync(path);
|
||||
const reader = new KPJFileReader();
|
||||
const kpj = reader.read(input);
|
||||
|
|
@ -63,7 +64,7 @@ describe('kpj-file-reader', function () {
|
|||
|
||||
// Test transform of .kpj into a KeymanDeveloperProject
|
||||
|
||||
const project = reader.transform(kpj);
|
||||
const project = reader.transform(kpjPath, kpj);
|
||||
|
||||
assert.equal(project.options.buildPath, '$PROJECTPATH/build');
|
||||
assert.isFalse(project.options.checkFilenameConventions);
|
||||
|
|
@ -74,7 +75,7 @@ describe('kpj-file-reader', function () {
|
|||
|
||||
assert.lengthOf(project.files, 2);
|
||||
|
||||
let f = project.files[0];
|
||||
let f: KeymanDeveloperProjectFile10 = <KeymanDeveloperProjectFile10>project.files[0];
|
||||
console.dir(f);
|
||||
assert.equal(f.id, 'id_f347675c33d2e6b1c705c787fad4941a');
|
||||
assert.equal(f.filename, 'khmer_angkor.kmn');
|
||||
|
|
@ -87,7 +88,7 @@ describe('kpj-file-reader', function () {
|
|||
assert.isUndefined(f.details.version);
|
||||
assert.lengthOf(f.childFiles, 1);
|
||||
|
||||
f = project.files[1];
|
||||
f = <KeymanDeveloperProjectFile10>project.files[1];
|
||||
assert.equal(f.id, 'id_8d4eb765f80c9f2b0f769cf4e4aaa456');
|
||||
assert.equal(f.filename, 'khmer_angkor.kps');
|
||||
assert.equal(f.filePath, 'source/khmer_angkor.kps');
|
||||
|
|
@ -99,7 +100,8 @@ describe('kpj-file-reader', function () {
|
|||
assert.isUndefined(f.details.version);
|
||||
assert.lengthOf(f.childFiles, 18);
|
||||
|
||||
f = project.files[0].childFiles[0];
|
||||
f = <KeymanDeveloperProjectFile10>project.files[0];
|
||||
f = <KeymanDeveloperProjectFile10>f.childFiles[0];
|
||||
assert.equal(f.id, 'id_8a1efc7c4ab7cfece8aedd847679ca27');
|
||||
assert.equal(f.filename, 'khmer_angkor.ico');
|
||||
assert.equal(f.filePath, 'source/khmer_angkor.ico');
|
||||
|
|
@ -108,7 +110,8 @@ describe('kpj-file-reader', function () {
|
|||
assert.isEmpty(f.details);
|
||||
assert.lengthOf(f.childFiles, 0);
|
||||
|
||||
f = project.files[1].childFiles[0];
|
||||
f = <KeymanDeveloperProjectFile10>project.files[1];
|
||||
f = <KeymanDeveloperProjectFile10>f.childFiles[0];
|
||||
assert.equal(f.id, 'id_8dc195db32d1fd0514de0ad51fff5df0');
|
||||
assert.equal(f.filename, 'khmer_angkor.js');
|
||||
assert.equal(f.filePath, 'source/../build/khmer_angkor.js');
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ cd "$THIS_SCRIPT_PATH"
|
|||
builder_describe \
|
||||
"Compiles the web-oriented utility function module." \
|
||||
"@/common/web/keyman-version" \
|
||||
configure clean build test \
|
||||
clean configure build test \
|
||||
"--ci For use with action ${BUILDER_TERM_START}test${BUILDER_TERM_END} - emits CI-friendly test reports"
|
||||
|
||||
builder_describe_outputs \
|
||||
|
|
|
|||
30
common/windows/build.sh
Executable file
30
common/windows/build.sh
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Compiles and tests the common Windows modules
|
||||
#
|
||||
|
||||
# Exit on command failure and when using unset variables:
|
||||
set -eu
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
. "${THIS_SCRIPT%/*}/../../resources/build/build-utils.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
#
|
||||
# TODO: when we have windows-specific tests, add them here
|
||||
# as child modules
|
||||
#
|
||||
|
||||
builder_describe "Keyman common Windows modules" \
|
||||
clean \
|
||||
configure \
|
||||
build \
|
||||
test
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
#-------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
builder_run_child_actions clean configure build test
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <sentry.h>
|
||||
|
||||
/*
|
||||
* Map of Sentry levels for events and breadcrumbs.
|
||||
*/
|
||||
|
|
@ -26,7 +28,7 @@ int keyman_sentry_main(bool is_keyman_developer, const char *logger, int argc, c
|
|||
int keyman_sentry_wmain(bool is_keyman_developer, const char *logger, int argc, wchar_t *argv[], int(*run)(int, wchar_t**));
|
||||
|
||||
void keyman_sentry_setexceptionfilter();
|
||||
void keyman_sentry_report_message(keyman_sentry_level_t level, const char *message, bool includeStack = false);
|
||||
sentry_uuid_t keyman_sentry_report_message(keyman_sentry_level_t level, const char *message, bool includeStack = false);
|
||||
void keyman_sentry_report_start();
|
||||
|
||||
//
|
||||
|
|
@ -37,3 +39,4 @@ void keyman_sentry_report_start();
|
|||
// d) privacy options are correctly checked
|
||||
//
|
||||
void keyman_sentry_test_crash();
|
||||
void keyman_sentry_test_message();
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ int keyman_sentry_init(bool is_keyman_developer, const char *logger) {
|
|||
|
||||
// Set the sentry-db-directory to a writeable location
|
||||
|
||||
char szPath[MAX_PATH + 64]; // sufficient length for sentry-0.4.9-db etc
|
||||
char szPath[MAX_PATH + 64]; // sufficient length for sentry-0.6.0-db etc
|
||||
|
||||
LPITEMIDLIST pidl;
|
||||
if (SUCCEEDED(SHGetFolderLocation(0, CSIDL_LOCAL_APPDATA, NULL, 0, &pidl))) {
|
||||
|
|
@ -43,7 +43,7 @@ int keyman_sentry_init(bool is_keyman_developer, const char *logger) {
|
|||
*p = 0;
|
||||
}
|
||||
|
||||
strcat_s(szPath, "sentry-0.4.9-db");
|
||||
strcat_s(szPath, "sentry-" SENTRY_SDK_VERSION "-db");
|
||||
sentry_options_set_database_path(options, szPath);
|
||||
}
|
||||
ILFree(pidl);
|
||||
|
|
@ -132,7 +132,7 @@ sentry_value_t CaptureStackTrace(PVOID TopAddr, DWORD FramesToSkip) {
|
|||
void keyman_sentry_report_exception(DWORD ExceptionCode, PVOID ExceptionAddress) {
|
||||
sentry_value_t event;
|
||||
const int FRAMES_TO_SKIP = 0;
|
||||
|
||||
sentry_uuid_t uuid = { 0 };
|
||||
char message[64];
|
||||
wsprintfA(message, "Exception %x at %p", (unsigned int) ExceptionCode, ExceptionAddress);
|
||||
|
||||
|
|
@ -159,18 +159,22 @@ void keyman_sentry_report_exception(DWORD ExceptionCode, PVOID ExceptionAddress)
|
|||
sentry_value_set_by_key(event, "threads", threads);
|
||||
}
|
||||
|
||||
sentry_capture_event(event);
|
||||
uuid = sentry_capture_event(event);
|
||||
}
|
||||
|
||||
fputs(message, stderr);
|
||||
fputs("\n", stderr);
|
||||
if(g_report_exceptions) {
|
||||
fputs("This error has been automatically reported to the Keyman team.\n", stderr);
|
||||
char uuid_string[37];
|
||||
sentry_uuid_as_string(&uuid, uuid_string);
|
||||
fprintf(stderr, "Sentry uuid: %s\n", uuid_string);
|
||||
}
|
||||
}
|
||||
|
||||
void keyman_sentry_report_message(keyman_sentry_level_t level, const char *message, bool includeStack) {
|
||||
sentry_uuid_t keyman_sentry_report_message(keyman_sentry_level_t level, const char *message, bool includeStack) {
|
||||
const int FRAMES_TO_SKIP = 0;
|
||||
sentry_uuid_t uuid = { 0 };
|
||||
|
||||
if ((g_report_exceptions && (level == SENTRY_LEVEL_ERROR || level == SENTRY_LEVEL_DEBUG)) || g_report_messages) {
|
||||
sentry_value_t event;
|
||||
|
|
@ -190,12 +194,15 @@ void keyman_sentry_report_message(keyman_sentry_level_t level, const char *messa
|
|||
}
|
||||
}
|
||||
|
||||
sentry_capture_event(event);
|
||||
uuid = sentry_capture_event(event);
|
||||
}
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/* Wrappers for main, wmain */
|
||||
|
||||
#define SENTRY_USE_LOCAL_FILTER
|
||||
#ifdef SENTRY_USE_LOCAL_FILTER
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER LastFilter;
|
||||
|
||||
|
|
@ -236,6 +243,11 @@ int keyman_sentry_main(bool is_keyman_developer, const char *logger, int argc, c
|
|||
keyman_sentry_test_crash();
|
||||
}
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "-sentry-client-test-message")) {
|
||||
// Undocumented test parameter
|
||||
keyman_sentry_test_message();
|
||||
}
|
||||
|
||||
int res = run(argc, argv);
|
||||
|
||||
keyman_sentry_shutdown();
|
||||
|
|
@ -253,6 +265,11 @@ int keyman_sentry_wmain(bool is_keyman_developer, const char *logger, int argc,
|
|||
keyman_sentry_test_crash();
|
||||
}
|
||||
|
||||
if (argc > 1 && !wcscmp(argv[1], L"-sentry-client-test-message")) {
|
||||
// Undocumented test parameter
|
||||
keyman_sentry_test_message();
|
||||
}
|
||||
|
||||
int res = run(argc, argv);
|
||||
|
||||
keyman_sentry_shutdown();
|
||||
|
|
@ -260,9 +277,19 @@ int keyman_sentry_wmain(bool is_keyman_developer, const char *logger, int argc,
|
|||
return res;
|
||||
}
|
||||
|
||||
void keyman_sentry_test_message() {
|
||||
fputs("Testing Sentry reporting:\n", stderr);
|
||||
auto uuid = keyman_sentry_report_message(KEYMAN_SENTRY_LEVEL_INFO, "Testing Sentry message reporting", true);
|
||||
char uuid_string[37];
|
||||
sentry_uuid_as_string(&uuid, uuid_string);
|
||||
fprintf(stderr, "Sentry uuid: %s\n", uuid_string);
|
||||
exit(0);
|
||||
}
|
||||
void keyman_sentry_test_crash() {
|
||||
fputs("Testing exception reporting:\n", stderr);
|
||||
fputs("Testing Sentry exception reporting:\n", stderr);
|
||||
RaiseException(0x0EA0BEEF, EXCEPTION_NONCONTINUABLE, 0, NULL);
|
||||
fputs("Should not have gotten here\n", stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Delay load sentry.dll from our subdirectory: #5166 */
|
||||
|
|
@ -274,12 +301,12 @@ void keyman_sentry_test_crash() {
|
|||
#else
|
||||
#define SENTRY_DLL SENTRY_BASE_DLL
|
||||
#endif
|
||||
#define SENTRY_INSTALL_PATH "sentry-0.4.9\\" SENTRY_DLL
|
||||
#define SENTRY_INSTALL_PATH "sentry-" SENTRY_SDK_VERSION "\\" SENTRY_DLL
|
||||
#define SENTRY_DEV_PATH "common\\windows\\delphi\\ext\\sentry\\" SENTRY_DLL
|
||||
#define ENV_KEYMAN_ROOT "KEYMAN_ROOT"
|
||||
|
||||
HMODULE LoadSentryLibrary() {
|
||||
//MAX_PATH + 64 chars leaves space for "\sentry-0.4.9\sentry.x64.dll"
|
||||
//MAX_PATH + 64 chars leaves space for "\sentry-0.6.0\sentry.x64.dll"
|
||||
char buf[MAX_PATH + 64], keyman_root[MAX_PATH + 64];
|
||||
|
||||
int nsize = GetModuleFileNameA(0, buf, MAX_PATH);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Keyman Third Party Package Readme: Sentry
|
|||
Name: Sentry
|
||||
Purpose: Sentry provides error reporting to a Sentry server
|
||||
Source: https://github.com/getsentry/sentry-native
|
||||
Version: 0.4.9
|
||||
Version: 0.6.0
|
||||
License: MIT
|
||||
Updated for: N/A
|
||||
Documentation: https://docs.sentry.io/platforms/native/
|
||||
|
|
@ -15,8 +15,8 @@ Include Paths: N/A
|
|||
Installation notes
|
||||
------------------
|
||||
|
||||
The original C++ version can be downloaded from site above. Source is
|
||||
not included
|
||||
The original C++ version can be downloaded from site above, and must be
|
||||
built following the instructions below.
|
||||
|
||||
Build notes
|
||||
-----------
|
||||
|
|
@ -27,6 +27,9 @@ team. This needs to be updated whenever Sentry is updated.
|
|||
Sentry.Client.pas is a wrapper of sentry.pas which provides a Delphi-style
|
||||
interface to Sentry.
|
||||
|
||||
Note: as of 0.6.0, performance SDKs are not included in Sentry.Client.pas,
|
||||
because we are not currently using them.
|
||||
|
||||
build_sentry_for_keyman.bat should be copied into the sentry-native repo,
|
||||
and tweaked as required for updated versions of Sentry. Run:
|
||||
|
||||
|
|
@ -37,6 +40,11 @@ and tweaked as required for updated versions of Sentry. Run:
|
|||
install: prepare install folder in sentry-native
|
||||
deploy: copy the sentry.dll and related files into this repo
|
||||
|
||||
**Important:** ensure that the sentry folder name in all .wxs files is
|
||||
updated to match the SENTRY_SDK_VERSION from sentry.h. Source C++ and .pas
|
||||
files use the constant to refer to the location where the files may be
|
||||
found.
|
||||
|
||||
License - MIT
|
||||
-------------
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -23,8 +23,14 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
/* SDK Version */
|
||||
#define SENTRY_SDK_NAME "sentry.native"
|
||||
#define SENTRY_SDK_VERSION "0.4.9"
|
||||
#ifndef SENTRY_SDK_NAME
|
||||
# ifdef __ANDROID__
|
||||
# define SENTRY_SDK_NAME "sentry.native.android"
|
||||
# else
|
||||
# define SENTRY_SDK_NAME "sentry.native"
|
||||
# endif
|
||||
#endif
|
||||
#define SENTRY_SDK_VERSION "0.6.0"
|
||||
#define SENTRY_SDK_USER_AGENT SENTRY_SDK_NAME "/" SENTRY_SDK_VERSION
|
||||
|
||||
/* common platform detection */
|
||||
|
|
@ -46,6 +52,10 @@ extern "C" {
|
|||
#elif defined(__linux) || defined(__linux__)
|
||||
# define SENTRY_PLATFORM_LINUX
|
||||
# define SENTRY_PLATFORM_UNIX
|
||||
#elif defined(_AIX)
|
||||
/* IBM i PASE is also counted as AIX */
|
||||
# define SENTRY_PLATFORM_AIX
|
||||
# define SENTRY_PLATFORM_UNIX
|
||||
#else
|
||||
# error unsupported platform
|
||||
#endif
|
||||
|
|
@ -189,7 +199,7 @@ SENTRY_API sentry_value_t sentry_value_new_int32(int32_t value);
|
|||
SENTRY_API sentry_value_t sentry_value_new_double(double value);
|
||||
|
||||
/**
|
||||
* Creates a new boolen value.
|
||||
* Creates a new boolean value.
|
||||
*/
|
||||
SENTRY_API sentry_value_t sentry_value_new_bool(int value);
|
||||
|
||||
|
|
@ -399,8 +409,8 @@ SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_thread(
|
|||
*
|
||||
* See https://develop.sentry.dev/sdk/event-payloads/stacktrace/
|
||||
*
|
||||
* The returned object needs to be attached to either an exception
|
||||
* event, or a thread object.
|
||||
* The returned object must be attached to either an exception or thread
|
||||
* object.
|
||||
*
|
||||
* If `ips` is NULL the current stack trace is captured, otherwise `len`
|
||||
* stack trace instruction pointers are attached to the event.
|
||||
|
|
@ -408,6 +418,17 @@ SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_thread(
|
|||
SENTRY_EXPERIMENTAL_API sentry_value_t sentry_value_new_stacktrace(
|
||||
void **ips, size_t len);
|
||||
|
||||
/**
|
||||
* Sets the Stack Trace conforming to the Stack Trace Interface in a value.
|
||||
*
|
||||
* The value argument must be either an exception or thread object.
|
||||
*
|
||||
* If `ips` is NULL the current stack trace is captured, otherwise `len` stack
|
||||
* trace instruction pointers are attached to the event.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_value_set_stacktrace(
|
||||
sentry_value_t value, void **ips, size_t len);
|
||||
|
||||
/**
|
||||
* Adds an Exception to an Event value.
|
||||
*
|
||||
|
|
@ -469,9 +490,12 @@ typedef struct sentry_ucontext_s {
|
|||
*
|
||||
* If the address is given in `addr` the stack is unwound form there.
|
||||
* Otherwise (NULL is passed) the current instruction pointer is used as
|
||||
* start address. The stack trace is written to `stacktrace_out` with up to
|
||||
* `max_len` frames being written. The actual number of unwound stackframes
|
||||
* is returned.
|
||||
* start address.
|
||||
* Unwinding with a given `addr` is not supported on all platforms.
|
||||
*
|
||||
* The stack trace in the form of instruction-addresses, is written to the
|
||||
* caller allocated `stacktrace_out`, with up to `max_len` frames being written.
|
||||
* The actual number of unwound stackframes is returned.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack(
|
||||
void *addr, void **stacktrace_out, size_t max_len);
|
||||
|
|
@ -479,8 +503,12 @@ SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack(
|
|||
/**
|
||||
* Unwinds the stack from the given context.
|
||||
*
|
||||
* The stack trace is written to `stacktrace_out` with up to `max_len` frames
|
||||
* being written. The actual number of unwound stackframes is returned.
|
||||
* The caller is responsible to construct an appropriate `sentry_ucontext_t`.
|
||||
* Unwinding from a user context is not supported on all platforms.
|
||||
*
|
||||
* The stack trace in the form of instruction-addresses, is written to the
|
||||
* caller allocated `stacktrace_out`, with up to `max_len` frames being written.
|
||||
* The actual number of unwound stackframes is returned.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API size_t sentry_unwind_stack_from_ucontext(
|
||||
const sentry_ucontext_t *uctx, void **stacktrace_out, size_t max_len);
|
||||
|
|
@ -543,13 +571,21 @@ typedef struct sentry_envelope_s sentry_envelope_t;
|
|||
SENTRY_API void sentry_envelope_free(sentry_envelope_t *envelope);
|
||||
|
||||
/**
|
||||
* Given an envelope returns the embedded event if there is one.
|
||||
* Given an Envelope, returns the embedded Event if there is one.
|
||||
*
|
||||
* This returns a borrowed value to the event in the envelope.
|
||||
* This returns a borrowed value to the Event in the Envelope.
|
||||
*/
|
||||
SENTRY_API sentry_value_t sentry_envelope_get_event(
|
||||
const sentry_envelope_t *envelope);
|
||||
|
||||
/**
|
||||
* Given an Envelope, returns the embedded Transaction if there is one.
|
||||
*
|
||||
* This returns a borrowed value to the Transaction in the Envelope.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API sentry_value_t sentry_envelope_get_transaction(
|
||||
const sentry_envelope_t *envelope);
|
||||
|
||||
/**
|
||||
* Serializes the envelope.
|
||||
*
|
||||
|
|
@ -571,7 +607,7 @@ SENTRY_API int sentry_envelope_write_to_file(
|
|||
/**
|
||||
* The Sentry Client Options.
|
||||
*
|
||||
* See https://docs.sentry.io/error-reporting/configuration/
|
||||
* See https://docs.sentry.io/platforms/native/configuration/
|
||||
*/
|
||||
struct sentry_options_s;
|
||||
typedef struct sentry_options_s sentry_options_t;
|
||||
|
|
@ -595,13 +631,16 @@ typedef struct sentry_options_s sentry_options_t;
|
|||
* * `startup_func`: This hook will be called by sentry inside of `sentry_init`
|
||||
* and instructs the transport to initialize itself. Failures will bubble up
|
||||
* to `sentry_init`.
|
||||
* * `flush_func`: Instructs the transport to flush its queue.
|
||||
* This hook receives a millisecond-resolution `timeout` parameter and should
|
||||
* return `0` if the transport queue is flushed within the timeout.
|
||||
* * `shutdown_func`: Instructs the transport to flush its queue and shut down.
|
||||
* This hook receives a millisecond-resolution `timeout` parameter and should
|
||||
* return `true` when the transport was flushed and shut down successfully.
|
||||
* In case of `false`, sentry will log an error, but continue with freeing the
|
||||
* transport.
|
||||
* return `0` if the transport is flushed and shut down successfully.
|
||||
* In case of a non-zero return value, sentry will log an error, but continue
|
||||
* with freeing the transport.
|
||||
* * `free_func`: Frees the transports `state`. This hook might be called even
|
||||
* though `shutdown_func` returned `false` previously.
|
||||
* though `shutdown_func` returned a failure code previously.
|
||||
*
|
||||
* The transport interface might be extended in the future with hooks to flush
|
||||
* its internal queue without shutting down, and to dump its internal queue to
|
||||
|
|
@ -641,6 +680,16 @@ SENTRY_API void sentry_transport_set_free_func(
|
|||
SENTRY_API void sentry_transport_set_startup_func(sentry_transport_t *transport,
|
||||
int (*startup_func)(const sentry_options_t *options, void *state));
|
||||
|
||||
/**
|
||||
* Sets the transport flush hook.
|
||||
*
|
||||
* This hook will receive a millisecond-resolution timeout.
|
||||
* It should return `0` if all the pending envelopes are
|
||||
* sent within the timeout, or `1` if the timeout is hit.
|
||||
*/
|
||||
SENTRY_API void sentry_transport_set_flush_func(sentry_transport_t *transport,
|
||||
int (*flush_func)(uint64_t timeout, void *state));
|
||||
|
||||
/**
|
||||
* Sets the transport shutdown hook.
|
||||
*
|
||||
|
|
@ -671,6 +720,20 @@ SENTRY_API void sentry_transport_free(sentry_transport_t *transport);
|
|||
SENTRY_API sentry_transport_t *sentry_new_function_transport(
|
||||
void (*func)(const sentry_envelope_t *envelope, void *data), void *data);
|
||||
|
||||
/**
|
||||
* This represents an interface for user-defined backends.
|
||||
*
|
||||
* Backends are responsible to handle crashes. They are maintained at runtime
|
||||
* via various life-cycle hooks from the sentry-core.
|
||||
*
|
||||
* At this point none of those interfaces are exposed in the API including
|
||||
* creation and destruction. The main use-case of the backend in the API at this
|
||||
* point is to disable it via `sentry_options_set_backend` at runtime before it
|
||||
* is initialized.
|
||||
*/
|
||||
struct sentry_backend_s;
|
||||
typedef struct sentry_backend_s sentry_backend_t;
|
||||
|
||||
/* -- Options APIs -- */
|
||||
|
||||
/**
|
||||
|
|
@ -707,11 +770,28 @@ SENTRY_API void sentry_options_set_transport(
|
|||
* call `sentry_value_decref` on the provided event, and return a
|
||||
* `sentry_value_new_null()` instead.
|
||||
*
|
||||
* If you have set an `on_crash` callback (independent of whether it discards or
|
||||
* retains the event), `before_send` will no longer be invoked for crash-events,
|
||||
* which allows you to better distinguish between crashes and all other events
|
||||
* in client-side pre-processing.
|
||||
*
|
||||
* This function may be invoked inside of a signal handler and must be safe for
|
||||
* that purpose, see https://man7.org/linux/man-pages/man7/signal-safety.7.html.
|
||||
* On Windows, it may be called from inside of a `UnhandledExceptionFilter`, see
|
||||
* the documentation on SEH (structured exception handling) for more information
|
||||
* https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling
|
||||
*
|
||||
* Up to version 0.4.18 the `before_send` callback wasn't invoked in case the
|
||||
* event sampling discarded an event. In the current implementation the
|
||||
* `before_send` callback is invoked even if the event sampling discards the
|
||||
* event, following the cross-SDK session filter order:
|
||||
*
|
||||
* https://develop.sentry.dev/sdk/sessions/#filter-order
|
||||
*
|
||||
* On Windows the crashpad backend can capture fast-fail crashes which by-pass
|
||||
* SEH. Since the `before_send` is called by a local exception-handler, it will
|
||||
* not be invoked when such a crash happened, even though a minidump will be
|
||||
* sent.
|
||||
*/
|
||||
typedef sentry_value_t (*sentry_event_function_t)(
|
||||
sentry_value_t event, void *hint, void *closure);
|
||||
|
|
@ -724,6 +804,64 @@ typedef sentry_value_t (*sentry_event_function_t)(
|
|||
SENTRY_API void sentry_options_set_before_send(
|
||||
sentry_options_t *opts, sentry_event_function_t func, void *data);
|
||||
|
||||
/**
|
||||
* Type of the `on_crash` callback.
|
||||
*
|
||||
* The `on_crash` callback replaces the `before_send` callback for crash events.
|
||||
* The interface is analogous to `before_send` in that the callback takes
|
||||
* ownership of the `event`, and should usually return that same event. In case
|
||||
* the event should be discarded, the callback needs to call
|
||||
* `sentry_value_decref` on the provided event, and return a
|
||||
* `sentry_value_new_null()` instead.
|
||||
*
|
||||
* Only the `inproc` backend currently fills the passed-in event with useful
|
||||
* data and processes any modifications to the return value. Since both
|
||||
* `breakpad` and `crashpad` use minidumps to capture the crash state, the
|
||||
* passed-in event is empty when using these backends, and they ignore any
|
||||
* changes to the return value.
|
||||
*
|
||||
* If you set this callback in the options, it prevents a concurrently enabled
|
||||
* `before_send` callback from being invoked in the crash case. This allows for
|
||||
* better differentiation between crashes and other events and gradual migration
|
||||
* from existing `before_send` implementations:
|
||||
*
|
||||
* - if you have a `before_send` implementation and do not define an `on_crash`
|
||||
* callback your application will receive both normal and crash events as
|
||||
* before
|
||||
* - if you have a `before_send` implementation but only want to handle normal
|
||||
* events with it, then you can define an `on_crash` callback that returns
|
||||
* the passed-in event and does nothing else
|
||||
* - if you are not interested in normal events, but only want to act on
|
||||
* crashes (within the limits mentioned below), then only define an
|
||||
* `on_crash` callback with the option to filter (on all backends) or enrich
|
||||
* (only inproc) the crash event
|
||||
*
|
||||
* This function may be invoked inside of a signal handler and must be safe for
|
||||
* that purpose, see https://man7.org/linux/man-pages/man7/signal-safety.7.html.
|
||||
* On Windows, it may be called from inside of a `UnhandledExceptionFilter`, see
|
||||
* the documentation on SEH (structured exception handling) for more information
|
||||
* https://docs.microsoft.com/en-us/windows/win32/debug/structured-exception-handling
|
||||
*
|
||||
* Platform-specific behavior:
|
||||
*
|
||||
* - does not work with crashpad on macOS.
|
||||
* - for breakpad on Linux the `uctx` parameter is always NULL.
|
||||
* - on Windows the crashpad backend can capture fast-fail crashes which
|
||||
* by-pass SEH. Since `on_crash` is called by a local exception-handler, it will
|
||||
* not be invoked when such a crash happened, even though a minidump will be
|
||||
* sent.
|
||||
*/
|
||||
typedef sentry_value_t (*sentry_crash_function_t)(
|
||||
const sentry_ucontext_t *uctx, sentry_value_t event, void *closure);
|
||||
|
||||
/**
|
||||
* Sets the `on_crash` callback.
|
||||
*
|
||||
* See the `sentry_crash_function_t` typedef above for more information.
|
||||
*/
|
||||
SENTRY_API void sentry_options_set_on_crash(
|
||||
sentry_options_t *opts, sentry_crash_function_t func, void *data);
|
||||
|
||||
/**
|
||||
* Sets the DSN.
|
||||
*/
|
||||
|
|
@ -738,6 +876,19 @@ SENTRY_API const char *sentry_options_get_dsn(const sentry_options_t *opts);
|
|||
* Sets the sample rate, which should be a double between `0.0` and `1.0`.
|
||||
* Sentry will randomly discard any event that is captured using
|
||||
* `sentry_capture_event` when a sample rate < 1 is set.
|
||||
*
|
||||
* The sampling happens at the end of the event processing according to the
|
||||
* following order:
|
||||
*
|
||||
* https://develop.sentry.dev/sdk/sessions/#filter-order
|
||||
*
|
||||
* Only items 3. to 6. are currently applicable to sentry-native. This means
|
||||
* each processing step is executed even if the sampling discards the event
|
||||
* before sending it to the backend. This is particularly relevant to users of
|
||||
* the `before_send` callback.
|
||||
*
|
||||
* The above is in contrast to versions up to 0.4.18 where the sampling happened
|
||||
* at the beginning of the processing/filter sequence.
|
||||
*/
|
||||
SENTRY_API void sentry_options_set_sample_rate(
|
||||
sentry_options_t *opts, double sample_rate);
|
||||
|
|
@ -999,6 +1150,29 @@ SENTRY_API void sentry_options_set_database_pathw(
|
|||
SENTRY_API void sentry_options_set_system_crash_reporter_enabled(
|
||||
sentry_options_t *opts, int enabled);
|
||||
|
||||
/**
|
||||
* Sets the maximum time (in milliseconds) to wait for the asynchronous tasks to
|
||||
* end on shutdown, before attempting a forced termination.
|
||||
*/
|
||||
SENTRY_API void sentry_options_set_shutdown_timeout(
|
||||
sentry_options_t *opts, uint64_t shutdown_timeout);
|
||||
|
||||
/**
|
||||
* Gets the maximum time (in milliseconds) to wait for the asynchronous tasks to
|
||||
* end on shutdown, before attempting a forced termination.
|
||||
*/
|
||||
SENTRY_API uint64_t sentry_options_get_shutdown_timeout(sentry_options_t *opts);
|
||||
|
||||
/**
|
||||
* Sets a user-defined backend.
|
||||
*
|
||||
* Since creation and destruction of backends is not exposed in the API, this
|
||||
* can only be used to set the backend to `NULL`, which disables the backend in
|
||||
* the initialization.
|
||||
*/
|
||||
SENTRY_API void sentry_options_set_backend(
|
||||
sentry_options_t *opts, sentry_backend_t *backend);
|
||||
|
||||
/* -- Global APIs -- */
|
||||
|
||||
/**
|
||||
|
|
@ -1012,6 +1186,15 @@ SENTRY_API void sentry_options_set_system_crash_reporter_enabled(
|
|||
*/
|
||||
SENTRY_API int sentry_init(sentry_options_t *options);
|
||||
|
||||
/**
|
||||
* Instructs the transport to flush its send queue.
|
||||
*
|
||||
* The `timeout` parameter is in milliseconds.
|
||||
*
|
||||
* Returns 0 on success, or a non-zero return value in case the timeout is hit.
|
||||
*/
|
||||
SENTRY_API int sentry_flush(uint64_t timeout);
|
||||
|
||||
/**
|
||||
* Shuts down the sentry client and forces transports to flush out.
|
||||
*
|
||||
|
|
@ -1080,6 +1263,10 @@ SENTRY_API sentry_user_consent_t sentry_user_consent_get(void);
|
|||
|
||||
/**
|
||||
* Sends a sentry event.
|
||||
*
|
||||
* If returns a nil UUID if the event being passed in is a transaction, and the
|
||||
* transaction will not be sent nor consumed. `sentry_transaction_finish` should
|
||||
* be used to send transactions.
|
||||
*/
|
||||
SENTRY_API sentry_uuid_t sentry_capture_event(sentry_value_t event);
|
||||
|
||||
|
|
@ -1154,16 +1341,48 @@ SENTRY_API void sentry_remove_fingerprint(void);
|
|||
*/
|
||||
SENTRY_API void sentry_set_transaction(const char *transaction);
|
||||
|
||||
/**
|
||||
* Removes the transaction.
|
||||
*/
|
||||
SENTRY_API void sentry_remove_transaction(void);
|
||||
|
||||
/**
|
||||
* Sets the event level.
|
||||
*/
|
||||
SENTRY_API void sentry_set_level(sentry_level_t level);
|
||||
|
||||
/**
|
||||
* Sets the maximum number of spans that can be attached to a
|
||||
* transaction.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_options_set_max_spans(
|
||||
sentry_options_t *opts, size_t max_spans);
|
||||
|
||||
/**
|
||||
* Gets the maximum number of spans that can be attached to a
|
||||
* transaction.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API size_t sentry_options_get_max_spans(
|
||||
sentry_options_t *opts);
|
||||
|
||||
/**
|
||||
* Sets the sample rate for transactions. Should be a double between
|
||||
* `0.0` and `1.0`. Transactions will be randomly discarded during
|
||||
* `sentry_transaction_finish` when the sample rate is < 1.0.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_options_set_traces_sample_rate(
|
||||
sentry_options_t *opts, double sample_rate);
|
||||
|
||||
/**
|
||||
* Returns the sample rate for transactions.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API double sentry_options_get_traces_sample_rate(
|
||||
sentry_options_t *opts);
|
||||
|
||||
/* -- Session APIs -- */
|
||||
|
||||
typedef enum {
|
||||
SENTRY_SESSION_STATUS_OK,
|
||||
SENTRY_SESSION_STATUS_CRASHED,
|
||||
SENTRY_SESSION_STATUS_ABNORMAL,
|
||||
SENTRY_SESSION_STATUS_EXITED,
|
||||
} sentry_session_status_t;
|
||||
|
||||
/**
|
||||
* Starts a new session.
|
||||
*/
|
||||
|
|
@ -1174,6 +1393,505 @@ SENTRY_API void sentry_start_session(void);
|
|||
*/
|
||||
SENTRY_API void sentry_end_session(void);
|
||||
|
||||
/**
|
||||
* Ends a session with an explicit `status` code.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_end_session_with_status(
|
||||
sentry_session_status_t status);
|
||||
|
||||
/* -- Performance Monitoring/Tracing APIs -- */
|
||||
|
||||
/**
|
||||
* A sentry Transaction Context.
|
||||
*
|
||||
* See Transaction Interface under
|
||||
* https://develop.sentry.dev/sdk/performance/#new-span-and-transaction-classes
|
||||
*/
|
||||
struct sentry_transaction_context_s;
|
||||
typedef struct sentry_transaction_context_s sentry_transaction_context_t;
|
||||
|
||||
/**
|
||||
* A sentry Transaction.
|
||||
*
|
||||
* See https://develop.sentry.dev/sdk/event-payloads/transaction/
|
||||
*/
|
||||
struct sentry_transaction_s;
|
||||
typedef struct sentry_transaction_s sentry_transaction_t;
|
||||
|
||||
/**
|
||||
* A sentry Span.
|
||||
*
|
||||
* See https://develop.sentry.dev/sdk/event-payloads/span/
|
||||
*/
|
||||
struct sentry_span_s;
|
||||
typedef struct sentry_span_s sentry_span_t;
|
||||
|
||||
/**
|
||||
* Constructs a new Transaction Context. The returned value needs to be passed
|
||||
* into `sentry_transaction_start` in order to be recorded and sent to sentry.
|
||||
*
|
||||
* See
|
||||
* https://docs.sentry.io/platforms/native/enriching-events/transaction-name/
|
||||
* for an explanation of a Transaction's `name`, and
|
||||
* https://develop.sentry.dev/sdk/performance/span-operations/ for conventions
|
||||
* around an `operation`'s value.
|
||||
*
|
||||
* Also see https://develop.sentry.dev/sdk/event-payloads/transaction/#anatomy
|
||||
* for an explanation of `operation`, in addition to other properties and
|
||||
* actions that can be performed on a Transaction.
|
||||
*
|
||||
* The returned value is not thread-safe. Users are expected to ensure that
|
||||
* appropriate locking mechanisms are implemented over the Transaction Context
|
||||
* if it needs to be mutated across threads. Methods operating on the
|
||||
* Transaction Context will mention what kind of expectations they carry if they
|
||||
* need to mutate or access the object in a thread-safe way.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API sentry_transaction_context_t *
|
||||
sentry_transaction_context_new(const char *name, const char *operation);
|
||||
|
||||
/**
|
||||
* Sets the `name` on a Transaction Context, which will be used in the
|
||||
* Transaction constructed off of the context.
|
||||
*
|
||||
* The Transaction Context should not be mutated by other functions while
|
||||
* setting a name on it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_name(
|
||||
sentry_transaction_context_t *tx_cxt, const char *name);
|
||||
|
||||
/**
|
||||
* Sets the `operation` on a Transaction Context, which will be used in the
|
||||
* Transaction constructed off of the context
|
||||
*
|
||||
* See https://develop.sentry.dev/sdk/performance/span-operations/ for
|
||||
* conventions on `operation`s.
|
||||
*
|
||||
* The Transaction Context should not be mutated by other functions while
|
||||
* setting an operation on it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_operation(
|
||||
sentry_transaction_context_t *tx_cxt, const char *operation);
|
||||
|
||||
/**
|
||||
* Sets the `sampled` field on a Transaction Context, which will be used in the
|
||||
* Transaction constructed off of the context.
|
||||
*
|
||||
* When passed any value above 0, the Transaction will bypass all sampling
|
||||
* options and always be sent to sentry. If passed 0, this Transaction and its
|
||||
* child spans will never be sent to sentry.
|
||||
*
|
||||
* The Transaction Context should not be mutated by other functions while
|
||||
* setting `sampled` on it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_set_sampled(
|
||||
sentry_transaction_context_t *tx_cxt, int sampled);
|
||||
|
||||
/**
|
||||
* Removes the `sampled` field on a Transaction Context, which will be used in
|
||||
* the Transaction constructed off of the context.
|
||||
*
|
||||
* The Transaction will use the sampling rate as defined in `sentry_options`.
|
||||
*
|
||||
* The Transaction Context should not be mutated by other functions while
|
||||
* removing `sampled`.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_remove_sampled(
|
||||
sentry_transaction_context_t *tx_cxt);
|
||||
|
||||
/**
|
||||
* Update the Transaction Context with the given HTTP header key/value pair.
|
||||
*
|
||||
* This is used to propagate distributed tracing metadata from upstream
|
||||
* services. Therefore, the headers of incoming requests should be fed into this
|
||||
* function so that sentry is able to continue a trace that was started by an
|
||||
* upstream service.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_context_update_from_header(
|
||||
sentry_transaction_context_t *tx_cxt, const char *key, const char *value);
|
||||
|
||||
/**
|
||||
* Starts a new Transaction based on the provided context, restored from an
|
||||
* external integration (i.e. a span from a different SDK) or manually
|
||||
* constructed by a user.
|
||||
*
|
||||
* The second parameter is a custom Sampling Context to be used with a Traces
|
||||
* Sampler to make a more informed sampling decision. The SDK does not currently
|
||||
* support a custom Traces Sampler and this parameter is ignored for the time
|
||||
* being but needs to be provided.
|
||||
*
|
||||
* Returns a Transaction, which is expected to be manually managed by the
|
||||
* caller. Manual management involves ensuring that `sentry_transaction_finish`
|
||||
* is invoked for the Transaction, and that the caller manually starts and
|
||||
* finishes any child Spans as needed on the Transaction.
|
||||
*
|
||||
* Not invoking `sentry_transaction_finish` with the returned Transaction means
|
||||
* it will be discarded, and will not be sent to sentry.
|
||||
*
|
||||
* To ensure that any Events or Message Events are associated with this
|
||||
* Transaction while it is active, invoke and pass in the Transaction returned
|
||||
* by this function to `sentry_set_transaction_object`. Further documentation on
|
||||
* this can be found in `sentry_set_transaction_object`'s docstring.
|
||||
*
|
||||
* Takes ownership of `transaction_context`. A Transaction Context cannot be
|
||||
* modified or re-used after it is used to start a Transaction.
|
||||
*
|
||||
* The returned value is not thread-safe. Users are expected to ensure that
|
||||
* appropriate locking mechanisms are implemented over the Transaction if it
|
||||
* needs to be mutated across threads. Methods operating on the Transaction will
|
||||
* mention what kind of expectations they carry if they need to mutate or access
|
||||
* the object in a thread-safe way.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API sentry_transaction_t *sentry_transaction_start(
|
||||
sentry_transaction_context_t *tx_cxt, sentry_value_t sampling_ctx);
|
||||
|
||||
/**
|
||||
* Finishes and sends a Transaction to sentry. The event ID of the Transaction
|
||||
* will be returned if this was successful; A nil UUID will be returned
|
||||
* otherwise.
|
||||
*
|
||||
* Always takes ownership of `transaction`, regardless of whether the operation
|
||||
* was successful or not. A Transaction cannot be modified or re-used after it
|
||||
* is finished.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API sentry_uuid_t sentry_transaction_finish(
|
||||
sentry_transaction_t *tx);
|
||||
|
||||
/**
|
||||
* Sets the Transaction so any Events sent while the Transaction
|
||||
* is active will be associated with the Transaction.
|
||||
*
|
||||
* If the Transaction being passed in is unsampled, it will still be associated
|
||||
* with any new Events. This will lead to some Events pointing to orphan or
|
||||
* missing traces in sentry, see
|
||||
* https://docs.sentry.io/product/sentry-basics/tracing/trace-view/#orphan-traces-and-broken-subtraces
|
||||
*
|
||||
* This increases the number of references pointing to the Transaction. Invoke
|
||||
* `sentry_transaction_finish` to remove the Transaction set by this function as
|
||||
* well as its reference by passing in the same Transaction as the one passed
|
||||
* into this function.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_set_transaction_object(
|
||||
sentry_transaction_t *tx);
|
||||
|
||||
/**
|
||||
* Sets the Span so any Events sent while the Span
|
||||
* is active will be associated with the Span.
|
||||
*
|
||||
* This increases the number of references pointing to the Span. Invoke
|
||||
* `sentry_span_finish` to remove the Span set by this function as well
|
||||
* as its reference by passing in the same Span as the one passed into
|
||||
* this function.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_set_span(sentry_span_t *span);
|
||||
|
||||
/**
|
||||
* Starts a new Span.
|
||||
*
|
||||
* The return value of `sentry_transaction_start` should be passed in as
|
||||
* `parent`.
|
||||
*
|
||||
* Both `operation` and `description` can be null, but it is recommended to
|
||||
* supply the former. See
|
||||
* https://develop.sentry.dev/sdk/performance/span-operations/ for conventions
|
||||
* around operations.
|
||||
*
|
||||
* See https://develop.sentry.dev/sdk/event-payloads/span/ for a description of
|
||||
* the created Span's properties and expectations for `operation` and
|
||||
* `description`.
|
||||
*
|
||||
* Returns a value that should be passed into `sentry_span_finish`. Not
|
||||
* finishing the Span means it will be discarded, and will not be sent to
|
||||
* sentry. `sentry_value_null` will be returned if the child Span could not be
|
||||
* created.
|
||||
*
|
||||
* To ensure that any Events or Message Events are associated with this
|
||||
* Span while it is active, invoke and pass in the Span returned
|
||||
* by this function to `sentry_set_span`. Further documentation on this can be
|
||||
* found in `sentry_set_span`'s docstring.
|
||||
*
|
||||
* This increases the number of references pointing to the Transaction.
|
||||
*
|
||||
* The returned value is not thread-safe. Users are expected to ensure that
|
||||
* appropriate locking mechanisms are implemented over the Span if it needs
|
||||
* to be mutated across threads. Methods operating on the Span will mention what
|
||||
* kind of expectations they carry if they need to mutate or access the object
|
||||
* in a thread-safe way.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API sentry_span_t *sentry_transaction_start_child(
|
||||
sentry_transaction_t *parent, char *operation, char *description);
|
||||
|
||||
/**
|
||||
* Starts a new Span.
|
||||
*
|
||||
* The return value of `sentry_span_start_child` may be passed in as `parent`.
|
||||
*
|
||||
* Both `operation` and `description` can be null, but it is recommended to
|
||||
* supply the former. See
|
||||
* https://develop.sentry.dev/sdk/performance/span-operations/ for conventions
|
||||
* around operations.
|
||||
*
|
||||
* See https://develop.sentry.dev/sdk/event-payloads/span/ for a description of
|
||||
* the created Span's properties and expectations for `operation` and
|
||||
* `description`.
|
||||
*
|
||||
* Returns a value that should be passed into `sentry_span_finish`. Not
|
||||
* finishing the Span means it will be discarded, and will not be sent to
|
||||
* sentry. `sentry_value_null` will be returned if the child Span could not be
|
||||
* created.
|
||||
*
|
||||
* To ensure that any Events or Message Events are associated with this
|
||||
* Span while it is active, invoke and pass in the Span returned
|
||||
* by this function to `sentry_set_span`. Further documentation on this can be
|
||||
* found in `sentry_set_span`'s docstring.
|
||||
*
|
||||
* The returned value is not thread-safe. Users are expected to ensure that
|
||||
* appropriate locking mechanisms are implemented over the Span if it needs
|
||||
* to be mutated across threads. Methods operating on the Span will mention what
|
||||
* kind of expectations they carry if they need to mutate or access the object
|
||||
* in a thread-safe way.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API sentry_span_t *sentry_span_start_child(
|
||||
sentry_span_t *parent, char *operation, char *description);
|
||||
|
||||
/**
|
||||
* Finishes a Span.
|
||||
*
|
||||
* This takes ownership of `span`. A Span cannot be modified or re-used after it
|
||||
* is finished.
|
||||
*
|
||||
* This will mutate the `span`'s containing Transaction, so the containing
|
||||
* Transaction should also not be mutated by other functions when finishing a
|
||||
* span.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_span_finish(sentry_span_t *span);
|
||||
|
||||
/**
|
||||
* Sets a tag on a Transaction to the given string value.
|
||||
*
|
||||
* Tags longer than 200 bytes will be truncated.
|
||||
*
|
||||
* The Transaction should not be mutated by other functions while a tag is being
|
||||
* set on it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_set_tag(
|
||||
sentry_transaction_t *transaction, const char *tag, const char *value);
|
||||
|
||||
/**
|
||||
* Removes a tag from a Transaction.
|
||||
*
|
||||
* The Transaction should not be mutated by other functions while a tag is being
|
||||
* removed from it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_remove_tag(
|
||||
sentry_transaction_t *transaction, const char *tag);
|
||||
|
||||
/**
|
||||
* Sets the given key in a Transaction's "data" section to the given value.
|
||||
*
|
||||
* The Transaction should not be mutated by other functions while data is being
|
||||
* set on it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_set_data(
|
||||
sentry_transaction_t *transaction, const char *key, sentry_value_t value);
|
||||
|
||||
/**
|
||||
* Removes a key from a Transaction's "data" section.
|
||||
*
|
||||
* The Transaction should not be mutated by other functions while data is being
|
||||
* removed from it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_remove_data(
|
||||
sentry_transaction_t *transaction, const char *key);
|
||||
|
||||
/**
|
||||
* Sets a tag on a Span to the given string value.
|
||||
*
|
||||
* Tags longer than 200 bytes will be truncated.
|
||||
*
|
||||
* The Span should not be mutated by other functions while a tag is being set on
|
||||
* it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_span_set_tag(
|
||||
sentry_span_t *span, const char *tag, const char *value);
|
||||
|
||||
/**
|
||||
* Removes a tag from a Span.
|
||||
*
|
||||
* The Span should not be mutated by other functions while a tag is being
|
||||
* removed from it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_span_remove_tag(
|
||||
sentry_span_t *span, const char *tag);
|
||||
|
||||
/**
|
||||
* Sets the given key in a Span's "data" section to the given value.
|
||||
*
|
||||
* The Span should not be mutated by other functions while data is being set on
|
||||
* it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_span_set_data(
|
||||
sentry_span_t *span, const char *key, sentry_value_t value);
|
||||
|
||||
/**
|
||||
* Removes a key from a Span's "data" section.
|
||||
*
|
||||
* The Span should not be mutated by other functions while data is being removed
|
||||
* from it.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_span_remove_data(
|
||||
sentry_span_t *span, const char *key);
|
||||
|
||||
/**
|
||||
* Sets a Transaction's name.
|
||||
*
|
||||
* The Transaction should not be mutated by other functions while setting its
|
||||
* name.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_set_name(
|
||||
sentry_transaction_t *transaction, const char *name);
|
||||
|
||||
/**
|
||||
* The status of a Span or Transaction.
|
||||
*
|
||||
* See https://develop.sentry.dev/sdk/event-payloads/span/ for documentation.
|
||||
*/
|
||||
typedef enum {
|
||||
// The operation completed successfully.
|
||||
// HTTP status 100..299 + successful redirects from the 3xx range.
|
||||
SENTRY_SPAN_STATUS_OK,
|
||||
// The operation was cancelled (typically by the user).
|
||||
SENTRY_SPAN_STATUS_CANCELLED,
|
||||
// Unknown. Any non-standard HTTP status code.
|
||||
// "We do not know whether the transaction failed or succeeded."
|
||||
SENTRY_SPAN_STATUS_UNKNOWN,
|
||||
// Client specified an invalid argument. 4xx.
|
||||
// Note that this differs from FailedPrecondition. InvalidArgument
|
||||
// indicates arguments that are problematic regardless of the
|
||||
// state of the system.
|
||||
SENTRY_SPAN_STATUS_INVALID_ARGUMENT,
|
||||
// Deadline expired before operation could complete.
|
||||
// For operations that change the state of the system, this error may be
|
||||
// returned even if the operation has been completed successfully.
|
||||
// HTTP redirect loops and 504 Gateway Timeout.
|
||||
SENTRY_SPAN_STATUS_DEADLINE_EXCEEDED,
|
||||
// 404 Not Found. Some requested entity (file or directory) was not found.
|
||||
SENTRY_SPAN_STATUS_NOT_FOUND,
|
||||
// Already exists (409)
|
||||
// Some entity that we attempted to create already exists.
|
||||
SENTRY_SPAN_STATUS_ALREADY_EXISTS,
|
||||
// 403 Forbidden
|
||||
// The caller does not have permission to execute the specified operation.
|
||||
SENTRY_SPAN_STATUS_PERMISSION_DENIED,
|
||||
// 429 Too Many Requests
|
||||
// Some resource has been exhausted, perhaps a per-user quota or perhaps
|
||||
// the entire file system is out of space.
|
||||
SENTRY_SPAN_STATUS_RESOURCE_EXHAUSTED,
|
||||
// Operation was rejected because the system is not in a state required for
|
||||
// the operation's execution.
|
||||
SENTRY_SPAN_STATUS_FAILED_PRECONDITION,
|
||||
// The operation was aborted, typically due to a concurrency issue.
|
||||
SENTRY_SPAN_STATUS_ABORTED,
|
||||
// Operation was attempted past the valid range.
|
||||
SENTRY_SPAN_STATUS_OUT_OF_RANGE,
|
||||
// 501 Not Implemented
|
||||
// Operation is not implemented or not enabled.
|
||||
SENTRY_SPAN_STATUS_UNIMPLEMENTED,
|
||||
// Other/generic 5xx
|
||||
SENTRY_SPAN_STATUS_INTERNAL_ERROR,
|
||||
// 503 Service Unavailable
|
||||
SENTRY_SPAN_STATUS_UNAVAILABLE,
|
||||
// Unrecoverable data loss or corruption
|
||||
SENTRY_SPAN_STATUS_DATA_LOSS,
|
||||
// 401 Unauthorized (actually does mean unauthenticated according to RFC
|
||||
// 7235)
|
||||
// Prefer PermissionDenied if a user is logged in.
|
||||
SENTRY_SPAN_STATUS_UNAUTHENTICATED,
|
||||
} sentry_span_status_t;
|
||||
|
||||
/**
|
||||
* Sets a Span's status.
|
||||
*
|
||||
* The Span should not be mutated by other functions while setting its status.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_span_set_status(
|
||||
sentry_span_t *span, sentry_span_status_t status);
|
||||
|
||||
/**
|
||||
* Sets a Transaction's status.
|
||||
*
|
||||
* The Transaction should not be mutated by other functions while setting its
|
||||
* status.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_set_status(
|
||||
sentry_transaction_t *tx, sentry_span_status_t status);
|
||||
|
||||
/**
|
||||
* Type of the `iter_headers` callback.
|
||||
*
|
||||
* The callback is being called with HTTP header key/value pairs.
|
||||
* These headers can be attached to outgoing HTTP requests to propagate
|
||||
* distributed tracing metadata to downstream services.
|
||||
*
|
||||
*/
|
||||
typedef void (*sentry_iter_headers_function_t)(
|
||||
const char *key, const char *value, void *userdata);
|
||||
|
||||
/**
|
||||
* Iterates the distributed tracing HTTP headers for the given span.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_span_iter_headers(sentry_span_t *span,
|
||||
sentry_iter_headers_function_t callback, void *userdata);
|
||||
|
||||
/**
|
||||
* Iterates the distributed tracing HTTP headers for the given transaction.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API void sentry_transaction_iter_headers(
|
||||
sentry_transaction_t *tx, sentry_iter_headers_function_t callback,
|
||||
void *userdata);
|
||||
|
||||
/**
|
||||
* Returns whether the application has crashed on the last run.
|
||||
*
|
||||
* Notes:
|
||||
* * The underlying value is set by sentry_init() - it must be called first.
|
||||
* * Call sentry_clear_crashed_last_run() to reset for the next app run.
|
||||
*
|
||||
* Possible return values:
|
||||
* 1 = the last run was a crash
|
||||
* 0 = no crash recognized
|
||||
* -1 = sentry_init() hasn't been called yet
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API int sentry_get_crashed_last_run(void);
|
||||
|
||||
/**
|
||||
* Clear the status of the "crashed-last-run". You should explicitly call
|
||||
* this after sentry_init() if you're using sentry_get_crashed_last_run().
|
||||
* Otherwise, the same information is reported on any subsequent runs.
|
||||
*
|
||||
* Notes:
|
||||
* * This doesn't change the value of sentry_get_crashed_last_run() yet.
|
||||
* However, if sentry_init() is called again, the value will change.
|
||||
* * This may only be called after sentry_init() and before sentry_close().
|
||||
*
|
||||
* Returns 0 on success, 1 on error.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API int sentry_clear_crashed_last_run(void);
|
||||
|
||||
/**
|
||||
* Sentry SDK version.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API const char *sentry_sdk_version(void);
|
||||
|
||||
/**
|
||||
* Sentry SDK name.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API const char *sentry_sdk_name(void);
|
||||
|
||||
/**
|
||||
* Sentry SDK User-Agent.
|
||||
*/
|
||||
SENTRY_EXPERIMENTAL_API const char *sentry_sdk_user_agent(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
|
|
@ -7,6 +7,7 @@ procedure main;
|
|||
implementation
|
||||
|
||||
uses
|
||||
sentry,
|
||||
Sentry.Client,
|
||||
System.SysUtils;
|
||||
|
||||
|
|
@ -29,7 +30,7 @@ begin
|
|||
//o.DSN := 'https://7b1ff1dae2c8495b84f90dadcf512b84@sentry.io/4853461';
|
||||
o.Release := 'keyman-14.0.22-alpha-local';
|
||||
o.HandlerPath := 'c:\Projects\keyman\app\windows\src\ext\sentry\test\Win32\Release\crashpad_handler.exe';
|
||||
o.DatabasePath := '.\sentry-0.4.9-db';
|
||||
o.DatabasePath := '.\sentry-'+SENTRY_SDK_VERSION+'-db';
|
||||
Result := TSentryClient.Create(o);
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@ begin
|
|||
end;
|
||||
|
||||
o.HandlerPath := ExtractFilePath(path) + 'crashpad_handler.exe';
|
||||
o.DatabasePath := TKeymanPaths.ErrorLogPath + 'sentry-0.4.9-db';
|
||||
o.DatabasePath := TKeymanPaths.ErrorLogPath + 'sentry-'+SENTRY_SDK_VERSION+'-db';
|
||||
|
||||
FClient := SentryClientClass.Create(o, ALogger, f);
|
||||
FClient.OnAfterEvent := ClientAfterEvent;
|
||||
|
|
|
|||
|
|
@ -93,12 +93,6 @@ if builder_is_dep_build || builder_has_option --no-tests; then
|
|||
builder_remove_dep /developer/src/kmc
|
||||
fi
|
||||
|
||||
if builder_is_debug_build; then
|
||||
CONFIGURATION=debug
|
||||
else
|
||||
CONFIGURATION=release
|
||||
fi
|
||||
|
||||
# 'mac' target builds both x86_64 and arm architectures and
|
||||
# generates a 'fat' library from them.
|
||||
builder_describe_internal_dependency \
|
||||
|
|
@ -106,20 +100,20 @@ builder_describe_internal_dependency \
|
|||
build:mac build:mac-arm64
|
||||
|
||||
builder_describe_outputs \
|
||||
configure:x86 /core/build/x86/$CONFIGURATION/build.ninja \
|
||||
configure:x64 /core/build/x64/$CONFIGURATION/build.ninja \
|
||||
configure:mac /core/build/mac/$CONFIGURATION/ \
|
||||
configure:mac-x86_64 /core/build/mac-x86_64/$CONFIGURATION/build.ninja \
|
||||
configure:mac-arm64 /core/build/mac-arm64/$CONFIGURATION/build.ninja \
|
||||
configure:arch /core/build/arch/$CONFIGURATION/build.ninja \
|
||||
configure:wasm /core/build/wasm/$CONFIGURATION/build.ninja \
|
||||
build:x86 /core/build/x86/$CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:x64 /core/build/x64/$CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:mac /core/build/mac/$CONFIGURATION/libkmnkbp0.a \
|
||||
build:mac-x86_64 /core/build/mac-x86_64/$CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:mac-arm64 /core/build/mac-arm64/$CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:arch /core/build/arch/$CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:wasm /core/build/wasm/$CONFIGURATION/src/libkmnkbp0.a
|
||||
configure:x86 /core/build/x86/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:x64 /core/build/x64/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:mac /core/build/mac/$BUILDER_CONFIGURATION/ \
|
||||
configure:mac-x86_64 /core/build/mac-x86_64/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:mac-arm64 /core/build/mac-arm64/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:arch /core/build/arch/$BUILDER_CONFIGURATION/build.ninja \
|
||||
configure:wasm /core/build/wasm/$BUILDER_CONFIGURATION/build.ninja \
|
||||
build:x86 /core/build/x86/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:x64 /core/build/x64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:mac /core/build/mac/$BUILDER_CONFIGURATION/libkmnkbp0.a \
|
||||
build:mac-x86_64 /core/build/mac-x86_64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:mac-arm64 /core/build/mac-arm64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:arch /core/build/arch/$BUILDER_CONFIGURATION/src/libkmnkbp0.a \
|
||||
build:wasm /core/build/wasm/$BUILDER_CONFIGURATION/src/libkmnkbp0.a
|
||||
|
||||
# Iterate through all possible targets; note that targets that cannot be built
|
||||
# on the current platform have already been excluded through the archtargets
|
||||
|
|
@ -129,7 +123,7 @@ targets=(wasm x86 x64 mac-x86_64 mac-arm64 arch)
|
|||
do_action() {
|
||||
local action_function=do_$1
|
||||
for target in "${targets[@]}"; do
|
||||
MESON_PATH="$KEYMAN_ROOT/core/build/$target/$CONFIGURATION"
|
||||
MESON_PATH="$KEYMAN_ROOT/core/build/$target/$BUILDER_CONFIGURATION"
|
||||
$action_function $target
|
||||
done
|
||||
}
|
||||
|
|
@ -145,7 +139,7 @@ do_action configure
|
|||
# After we have built the necessary internal dependencies, then we can go
|
||||
# ahead and build a fat library for external consumption
|
||||
if builder_start_action configure:mac; then
|
||||
mkdir -p "$KEYMAN_ROOT/core/build/mac/$CONFIGURATION"
|
||||
mkdir -p "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION"
|
||||
builder_finish_action success configure:mac
|
||||
fi
|
||||
|
||||
|
|
@ -155,9 +149,9 @@ do_action build
|
|||
|
||||
if builder_start_action build:mac; then
|
||||
lipo -create \
|
||||
"$KEYMAN_ROOT/core/build/mac-x86_64/$CONFIGURATION/src/libkmnkbp0.a" \
|
||||
"$KEYMAN_ROOT/core/build/mac-arm64/$CONFIGURATION/src/libkmnkbp0.a" \
|
||||
-output "$KEYMAN_ROOT/core/build/mac/$CONFIGURATION/libkmnkbp0.a"
|
||||
"$KEYMAN_ROOT/core/build/mac-x86_64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a" \
|
||||
"$KEYMAN_ROOT/core/build/mac-arm64/$BUILDER_CONFIGURATION/src/libkmnkbp0.a" \
|
||||
-output "$KEYMAN_ROOT/core/build/mac/$BUILDER_CONFIGURATION/libkmnkbp0.a"
|
||||
builder_finish_action success build:mac
|
||||
fi
|
||||
|
||||
|
|
@ -170,7 +164,7 @@ if builder_start_action test:mac; then
|
|||
# assume that build:mac has run so both architectures will be
|
||||
# available
|
||||
target=mac-`uname -m`
|
||||
MESON_PATH="$KEYMAN_ROOT/core/build/$target/$CONFIGURATION"
|
||||
MESON_PATH="$KEYMAN_ROOT/core/build/$target/$BUILDER_CONFIGURATION"
|
||||
meson test -C "$MESON_PATH" "${builder_extra_params[@]}"
|
||||
builder_finish_action success test:mac
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -37,11 +37,11 @@ do_configure() {
|
|||
fi
|
||||
|
||||
if [[ $target =~ ^(x86|x64)$ ]]; then
|
||||
cmd //C build.bat $target $CONFIGURATION configure $BUILD_BAT_keyman_core_tests "${builder_extra_params[@]}"
|
||||
cmd //C build.bat $target $BUILDER_CONFIGURATION configure $BUILD_BAT_keyman_core_tests "${builder_extra_params[@]}"
|
||||
else
|
||||
pushd "$THIS_SCRIPT_PATH" > /dev/null
|
||||
# Additional arguments are used by Linux build, e.g. -Dprefix=${INSTALLDIR}
|
||||
meson setup "$MESON_PATH" $MESON_CROSS_FILE --werror --buildtype $CONFIGURATION $STANDARD_MESON_ARGS "${builder_extra_params[@]}"
|
||||
meson setup "$MESON_PATH" $MESON_CROSS_FILE --werror --buildtype $BUILDER_CONFIGURATION $STANDARD_MESON_ARGS "${builder_extra_params[@]}"
|
||||
popd > /dev/null
|
||||
fi
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ do_build() {
|
|||
local target=$1
|
||||
builder_start_action build:$target || return 0
|
||||
if [[ $target =~ ^(x86|x64)$ ]]; then
|
||||
cmd //C build.bat $target $CONFIGURATION build "${builder_extra_params[@]}"
|
||||
cmd //C build.bat $target $BUILDER_CONFIGURATION build "${builder_extra_params[@]}"
|
||||
elif $MESON_LOW_VERSION; then
|
||||
pushd "$MESON_PATH" > /dev/null
|
||||
ninja
|
||||
|
|
@ -75,7 +75,7 @@ do_test() {
|
|||
local target=$1
|
||||
builder_start_action test:$target || return 0
|
||||
if [[ $target =~ ^(x86|x64)$ ]]; then
|
||||
cmd //C build.bat $target $CONFIGURATION test "${builder_extra_params[@]}"
|
||||
cmd //C build.bat $target $BUILDER_CONFIGURATION test "${builder_extra_params[@]}"
|
||||
else
|
||||
meson test -C "$MESON_PATH" "${builder_extra_params[@]}"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -724,7 +724,6 @@ COMP_KMXPLUS_LIST::valid(KMX_DWORD _kmn_unused(length)) const {
|
|||
assert(false);
|
||||
return false;
|
||||
}
|
||||
// TODO-LDML: further validation in the COMP_KMXPLUS_LIST_Helper class
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -892,7 +891,7 @@ KMX_DWORD COMP_KMXPLUS_STRS::find(const std::u16string& s) const {
|
|||
if (s.empty()) {
|
||||
return 0; // shortcut
|
||||
}
|
||||
// TODO-LDML: You're not going to search these linearly, reinterpreting each time??!
|
||||
// TODO-LDML: suboptimal, but currently only run from the test runner. Could be a binary search since the strings are already in codepoint order.
|
||||
for (KMX_DWORD i = 0; i<count; i++) {
|
||||
if (s == get(i)) {
|
||||
return i;
|
||||
|
|
|
|||
|
|
@ -491,11 +491,6 @@ For each key:
|
|||
TODO-LDML: Note that conforming to other keyman values, left versus right shift
|
||||
cannot be distinguished.
|
||||
|
||||
TODO-LDML: Note that 'Current' LDML spec allows `shiftL`/`shiftR`, `opt`,
|
||||
and `cmd` but there is a request to drop these. These four are not represented
|
||||
here.
|
||||
|
||||
|
||||
### C7043.2.16 `list`—String lists
|
||||
|
||||
| ∆ | Bits | Name | Description |
|
||||
|
|
@ -532,6 +527,5 @@ These indices are a pool of indexes into the string table.
|
|||
The strings order are significant. There is not a 'null' string at the end of each list.
|
||||
|
||||
## TODO-LDML: various things that need to be completed here or fixed in-spec
|
||||
|
||||
> * UnicodeSets
|
||||
> * spec: ABNT2 key has hex value 0xC1 (even if kbdus.dll doesn't produce that)
|
||||
|
|
|
|||
|
|
@ -10,26 +10,27 @@
|
|||
<version number="1.0.0" />
|
||||
<info author="Team Keyboard" normalization="NFC" layout="AZERTY" indicator="FR" />
|
||||
<names>
|
||||
<name value="French" />
|
||||
<name value="French AZERTY" />
|
||||
<name value="French Test" />
|
||||
<name value="French Test AZERTY" />
|
||||
</names>
|
||||
|
||||
<settings fallback="omit" transformPartial="hide" /> <!-- "hide" makes this act like a dead keyTBD -->
|
||||
|
||||
<vkeys>
|
||||
<vkey from="Q" to="A" />
|
||||
<vkey from="W" to="Z" />
|
||||
<vkey from="A" to="Q" />
|
||||
<vkey from="Z" to="W" />
|
||||
<!-- <vkey from="BKQUOTE" to="QUOTE" /> -->
|
||||
<vkey from="Q" to="A" />
|
||||
<vkey from="W" to="Z" />
|
||||
<vkey from="A" to="Q" />
|
||||
<vkey from="Z" to="W" />
|
||||
<vkey from="GRAVE" to="QUOTE" /> <!-- TODO-LDML: GRAVE=BKQUOTE in ldml? -->
|
||||
<!-- <vkey from="MINUS" to="LBRACKET" /> --> <!-- TODO-LDML: missing MINUS, LBRKT=LBRACKET in ldml? -->
|
||||
<!-- i.e., OEM_3 >>> OEM_7 -->
|
||||
<!-- <vkey from="MINUS" to="LBRKT" /> -->
|
||||
<!-- <vkey from="LBRKT" to="RBRKT" /> -->
|
||||
<!-- <vkey from="COLON" to="M" /> -->
|
||||
<!-- <vkey from="QUOTE" to="BKQUOTE" /> -->
|
||||
<vkey from="M" to="COMMA" />
|
||||
<vkey from="LBRACKET" to="RBRACKET" /> <!-- TODO-LDML: RBRKT=RBRACKET in ldml? -->
|
||||
<!-- <vkey from="COLON" to="M" /> --> <!-- TODO-LDML: missing COLON-->
|
||||
<vkey from="QUOTE" to="GRAVE" /> <!-- TODO-LDML: GRAVE=BKQUOTE in ldml? -->
|
||||
<vkey from="M" to="COMMA" />
|
||||
<vkey from="COMMA" to="PERIOD" />
|
||||
<vkey from="PERIOD" to="SLASH" />
|
||||
<!-- <vkey from="SLASH" to="EXCLAMATION" /> -->
|
||||
<!-- <vkey from="SLASH" to="EXCLAMATION" /> --> <!-- TODO-LDML: missing: EXCLAMATION -->
|
||||
<!-- i.e., OEM_2 >>> OEM_8 -->
|
||||
</vkeys>
|
||||
|
||||
|
|
@ -48,61 +49,59 @@
|
|||
</displays>
|
||||
|
||||
<keys>
|
||||
<!-- TODO-LDML: begin additions -->
|
||||
<import base="cldr" path="techpreview/keys-Zyyy-punctuation.xml"/>
|
||||
<key id="asterisk" to="*" />
|
||||
<key id="u-grave" to="ü" />
|
||||
<key id="section" to="§" />
|
||||
<key id="degree" to="°" />
|
||||
<key id="micro" to="µ" />
|
||||
<key id="shift" switch="shift"/> <!-- TODO-LDML: implied key? -->
|
||||
<key id="numeric" switch="numeric"/> <!-- TODO-LDML: implied key? -->
|
||||
<key id="symbol" switch="symbol"/> <!-- TODO-LDML: implied key? -->
|
||||
<key id="base" switch="base"/> <!-- TODO-LDML: implied key? -->
|
||||
<key id="bksp" gap="true" /> <!-- TODO-LDML: ?!! -->
|
||||
<key id="extra" gap="true" /> <!-- TODO-LDML: ?!! -->
|
||||
<key id="enter" to="\u{000A}" /> <!-- TODO-LDML: ?!! -->
|
||||
<key id="yen" to="¥" />
|
||||
<key id="bullet" to="•" />
|
||||
<import base="cldr" path="techpreview/keys-Zyyy-punctuation.xml" />
|
||||
<import base="cldr" path="techpreview/keys-Zyyy-currency.xml" />
|
||||
|
||||
<!-- TODO-LDML: end additions -->
|
||||
<key id="super-2" to="²" longPress="₂" />
|
||||
<key id="amp" to="&" /> <!-- 1 -->
|
||||
<!-- switch keys -->
|
||||
<key id="shift" switch="shift" />
|
||||
<key id="numeric" switch="numeric" />
|
||||
<key id="symbol" switch="symbol" />
|
||||
<key id="base" switch="base" />
|
||||
|
||||
<!-- TODO-LDML -->
|
||||
<key id="bksp" gap="true" />
|
||||
<!-- TODO-LDML -->
|
||||
<key id="extra" gap="true" />
|
||||
<!-- TODO-LDML -->
|
||||
<key id="enter" to="\u{000A}" />
|
||||
|
||||
<!-- extra keys -->
|
||||
<key id="u-grave" to="ü" />
|
||||
<key id="e-grave" to="é" /> <!-- 2 -->
|
||||
<key id="quote" to="\u{22}" /> <!-- 3 to= " -->
|
||||
<key id="apos" to="'" /> <!-- 4 -->
|
||||
<key id="open-paren" to="(" /> <!-- 5 -->
|
||||
<key id="hyphen" to="-" /> <!-- 6 -->
|
||||
<key id="e-acute" to="è" /> <!-- 7 -->
|
||||
<key id="underscore" to="_" /> <!-- 8 -->
|
||||
<key id="c-cedilla" to="ç" /> <!-- 9 -->
|
||||
<key id="a-acute" to="à" /> <!-- 0 -->
|
||||
<key id="close-paren" to=")" /> <!-- - -->
|
||||
<key id="equal" to="=" />
|
||||
|
||||
<!-- extra symbols -->
|
||||
<key id="bullet" to="•" />
|
||||
<key id="umlaut" to="¨" />
|
||||
<key id="a" flicks="a" to="a" longPress="à â á ä ã å ā" />
|
||||
<key id="super-2" to="²" longPress="₂" />
|
||||
|
||||
<!-- test key -->
|
||||
<key id="a" flicks="a" to="a" longPress="à â á ä ã å ā" />
|
||||
<flicks id="a">
|
||||
<flick directions="nw" to="\u1234" />
|
||||
<flick directions="nw se" to="\uFFFF" />
|
||||
<flick directions="e" to="\uFFF0" />
|
||||
</flicks>
|
||||
|
||||
<!-- test key -->
|
||||
<key id="A" flicks="b" to="A" longPress="À Á Ä Ã Å Ā" />
|
||||
|
||||
<!-- test flick -->
|
||||
<flicks id="b">
|
||||
<flick directions="nw" to="\u4567" />
|
||||
<flick directions="nw se" to="\uFFFF" />
|
||||
<flick directions="e" to="\uFFF0" />
|
||||
</flicks>
|
||||
|
||||
<key id="gap" gap="true" width="1.0" />
|
||||
<!-- TODO: all additional maps, hardware and touch -->
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="iso">
|
||||
<layers form="iso">
|
||||
<!-- in DTD: required if conformsTo ≥ 41 -->
|
||||
<layer modifier="none">
|
||||
<row keys="super-2 amp e-grave quote apos open-paren hyphen e-acute underscore c-cedilla a-acute close-paren equal" />
|
||||
<row keys="super-2 amp e-grave double-quote apos open-paren hyphen e-acute underscore c-cedilla a-acute close-paren equal" />
|
||||
<row keys="a z e r t y u i o p caret dollar" />
|
||||
<row keys="q s d f g h j k l m u-grave asterisk" />
|
||||
<row keys="open-angle w x c v b n comma semi-colon colon bang" />
|
||||
|
|
@ -123,28 +122,28 @@
|
|||
<layer id="base">
|
||||
<row keys="a z e r t y u i o p" />
|
||||
<row keys="q s d f g h j k l m" />
|
||||
<row keys="shift gap w x c v b n gap bksp" />
|
||||
<row keys="numeric extra space enter" /> <!-- The default "space" definition includes `stretch="true"` -->
|
||||
<row keys="shift gap w x c v b n gap" /> <!--TODO: + bksp -->
|
||||
<row keys="numeric extra space enter" />
|
||||
</layer>
|
||||
|
||||
<layer id="shift">
|
||||
<row keys="A Z E R T Y U I O P" />
|
||||
<row keys="Q S D F G H J K L M" />
|
||||
<row keys="base W X C V B N bksp" />
|
||||
<row keys="base W X C V B N" /> <!--TODO: + bksp -->
|
||||
<row keys="numeric extra space enter" />
|
||||
</layer>
|
||||
|
||||
<layer id="numeric">
|
||||
<row keys="1 2 3 4 5 6 7 8 9 0" />
|
||||
<row keys="hyphen slash colon semi-colon open-paren close-paren dollar amp at quote" />
|
||||
<row keys="symbol period comma question bang quote bksp" />
|
||||
<row keys="hyphen slash colon semi-colon open-paren close-paren dollar amp at double-quote" />
|
||||
<row keys="symbol period comma question bang double-quote" /> <!--TODO: + bksp -->
|
||||
<row keys="base extra space enter" />
|
||||
</layer>
|
||||
|
||||
<layer id="symbol">
|
||||
<row keys="open-square close-square open-curly close-curly hash percent caret asterisk plus equal" />
|
||||
<row keys="underscore backslash pipe tilde open-angle close-angle euro pound yen bullet" />
|
||||
<row keys="numeric period comma question bang quote bksp" />
|
||||
<row keys="numeric period comma question bang double-quote" /> <!--TODO: + bksp -->
|
||||
<row keys="base extra space enter" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<key id="that" to="ថា" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<row keys="hmaqtugha that" /> <!-- number row -->
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
<key id="screamcat" to="🙀" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<row keys="hmaqtugha that screamcat" /> <!-- number row -->
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ Note: keys should start with [K_A][K_B][K_C] not [K_Y] when transforms are funct
|
|||
<key id="hyphen" to="-"/>
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<row keys="space" /> <!-- number row -->
|
||||
<row keys="q w e r t y u i o p" />
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<key id="eee" to="e" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base" modifier="none">
|
||||
<row keys="hmaqtugha that" /> <!-- number row -->
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<keys/> <!-- implied only -->
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base" modifier="none">
|
||||
<row keys="a" /> <!-- K_BKQUOTE -->
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ Gets part of the way,
|
|||
Note this is the 47-key version.
|
||||
-->
|
||||
<!DOCTYPE keyboard SYSTEM "../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard.dtd">
|
||||
<keyboard locale="mt" conformsTo="techpreview">
|
||||
<!-- remainder entirely from resources/standards-data/ldml-keyboards/techpreview/3.0/mt-t-k0-47key.xml -->
|
||||
<keyboard locale="mt-t-k0-47key" conformsTo="techpreview">
|
||||
<locales>
|
||||
<!-- English is also an official language in Malta.-->
|
||||
<locale id="en" />
|
||||
|
|
@ -19,16 +20,15 @@ Gets part of the way,
|
|||
<info author="Steven R. Loomis" normalization="NFC" layout="QWERTY" indicator="MT" />
|
||||
|
||||
<names>
|
||||
<name value="Maltese (47-key)" />
|
||||
<!-- <name value="MSA 100:2002" /> -->
|
||||
<name value="Maltese 47-key" />
|
||||
<name value="MSA 100:2002 47-key" />
|
||||
</names>
|
||||
|
||||
<keys>
|
||||
<!-- imports -->
|
||||
<import base="cldr" path="techpreview/keys-Zyyy-punctuation.xml"/>
|
||||
<import base="cldr" path="techpreview/keys-Zyyy-punctuation.xml" />
|
||||
<import base="cldr" path="techpreview/keys-Zyyy-currency.xml" />
|
||||
|
||||
<!-- TODO-LDML: added next (but it is in a pending CLDR PR) #2652 -->
|
||||
<key id="asterisk" to="*" />
|
||||
<!-- accent grave -->
|
||||
<key id="a-grave" to="à" />
|
||||
<key id="A-grave" to="À" />
|
||||
|
|
@ -41,8 +41,8 @@ Gets part of the way,
|
|||
<key id="u-grave" to="ù" />
|
||||
<key id="U-grave" to="Ù" />
|
||||
|
||||
<!-- tikka and maqtua -->
|
||||
<key id="c-tikka" to="ċ" />
|
||||
<!-- tikka and maqtugha -->
|
||||
<key id="c-tikka" to="ċ" /> <!-- tikka is a dot -->
|
||||
<key id="C-tikka" to="Ċ" />
|
||||
<key id="g-tikka" to="ġ" />
|
||||
<key id="G-tikka" to="Ġ" />
|
||||
|
|
@ -53,13 +53,13 @@ Gets part of the way,
|
|||
|
||||
<!-- Cedilla -->
|
||||
<key id="c-cedilla" to="ç" />
|
||||
|
||||
<!-- gaps -->
|
||||
<key id="gap" gap="true" width="1" />
|
||||
</keys>
|
||||
|
||||
|
||||
<!-- TODO-LDML: This has been modified to be the US 47-key format.
|
||||
See MSA 100:2002, Appendix, figure A.1 -->
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<!-- 47-key: MSA 100:2002, Appendix, figure A.1 -->
|
||||
<layers form="us">
|
||||
<layer modifier="none">
|
||||
<row keys="c-tikka 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
|
||||
<row keys="q w e r t y u i o p g-tikka h-maqtugha z-tikka" />
|
||||
|
|
@ -84,7 +84,7 @@ Gets part of the way,
|
|||
<row keys="space" />
|
||||
</layer>
|
||||
|
||||
<layer modifier="altR shift"> <!-- TODO-LDML: disallowed by current DTD but see CLDR https://github.com/unicode-org/cldr/pull/2661 -->
|
||||
<layer modifier="altR shift">
|
||||
<row keys="tilde gap gap gap gap gap gap gap gap gap gap gap gap" />
|
||||
<row keys="gap gap E-grave gap gap gap U-grave I-grave O-grave gap open-curly close-curly pipe" />
|
||||
<row keys="A-grave gap gap gap gap gap gap gap gap gap gap" />
|
||||
|
|
@ -92,4 +92,5 @@ Gets part of the way,
|
|||
<row keys="space" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
||||
</keyboard>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ Gets part of the way,
|
|||
Exact copy of mt.xml from CLDR, but with:
|
||||
- an updated DTD path
|
||||
- test case
|
||||
- changed 'altR-shift' to 'altR shift'
|
||||
-->
|
||||
<!DOCTYPE keyboard SYSTEM "../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard.dtd">
|
||||
<keyboard locale="mt" conformsTo="techpreview">
|
||||
|
|
@ -21,13 +20,14 @@ Exact copy of mt.xml from CLDR, but with:
|
|||
<info author="Steven R. Loomis" normalization="NFC" layout="QWERTY" indicator="MT" />
|
||||
|
||||
<names>
|
||||
<name value="Maltese (48-key)" />
|
||||
<!-- <name value="MSA 100:2002" /> -->
|
||||
<name value="Maltese" />
|
||||
<name value="MSA 100:2002" />
|
||||
</names>
|
||||
|
||||
<keys>
|
||||
<!-- imports -->
|
||||
<import base="cldr" path="techpreview/keys-Zyyy-punctuation.xml"/>
|
||||
<import base="cldr" path="techpreview/keys-Zyyy-currency.xml"/>
|
||||
|
||||
<!-- accent grave -->
|
||||
<key id="a-grave" to="à" />
|
||||
|
|
@ -41,8 +41,8 @@ Exact copy of mt.xml from CLDR, but with:
|
|||
<key id="u-grave" to="ù" />
|
||||
<key id="U-grave" to="Ù" />
|
||||
|
||||
<!-- tikka and maqtua -->
|
||||
<key id="c-tikka" to="ċ" />
|
||||
<!-- tikka and maqtugha -->
|
||||
<key id="c-tikka" to="ċ" /> <!-- tikka is a dot -->
|
||||
<key id="C-tikka" to="Ċ" />
|
||||
<key id="g-tikka" to="ġ" />
|
||||
<key id="G-tikka" to="Ġ" />
|
||||
|
|
@ -55,7 +55,8 @@ Exact copy of mt.xml from CLDR, but with:
|
|||
<key id="c-cedilla" to="ç" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="iso">
|
||||
<!-- 48-key -->
|
||||
<layers form="iso">
|
||||
<layer modifier="none">
|
||||
<row keys="c-tikka 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
|
||||
<row keys="q w e r t y u i o p g-tikka h-maqtugha" />
|
||||
|
|
@ -88,4 +89,5 @@ Exact copy of mt.xml from CLDR, but with:
|
|||
<row keys="space" />
|
||||
</layer>
|
||||
</layers>
|
||||
|
||||
</keyboard>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<key id="gap" gap="true" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<row keys="a gap"/>
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<!-- no keys listed, keys-Latn-implied.xml will be implicitly imported -->
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<row keys="a" /> <!-- number row -->
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<name value="TestKbd" />
|
||||
</names>
|
||||
<!-- only implied keys - the <keys/> element is omitted. -->
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<row keys="a" /> <!-- number row -->
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -528,7 +528,7 @@ int LdmlJsonTestSourceFactory::load(const km::kbp::path &compiled, const km::kbp
|
|||
// TODO-LDML: store these elsewhere?
|
||||
std::cout << "JSON: reading " << info_name << " test of " << info_keyboard << " by " << info_author << std::endl;
|
||||
|
||||
// TODO-LDML: repertoire test
|
||||
// TODO-LDML: repertoire test #8435
|
||||
|
||||
auto all_tests = data["/keyboardTest/tests"_json_pointer];
|
||||
assert_or_return((!all_tests.empty()) && (all_tests.size() > 0));
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ enum ldml_action_type {
|
|||
* expected text
|
||||
*/
|
||||
LDML_ACTION_CHECK_EXPECTED,
|
||||
// TODO-LDML: gestures, etc?
|
||||
// TODO-LDML: gestures, etc? Depends on touch.
|
||||
};
|
||||
|
||||
struct ldml_action {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ begin
|
|||
if m.Success then
|
||||
begin
|
||||
msgFilename := m.Groups[1].Value;
|
||||
msgLine := StrToInt(m.Groups[2].Value);
|
||||
msgLine := StrToIntDef(m.Groups[2].Value, 0);
|
||||
msgType := m.Groups[3].Value;
|
||||
msgCode := StrToInt('$'+m.Groups[4].Value);
|
||||
msgText := m.Groups[5].Value;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@
|
|||
<Directory Id="KeymanRoot" Name="Keyman">
|
||||
<Directory Id="KEYMANDEV" Name="Keyman Developer">
|
||||
<Directory Id="INSTALLDIR" Name="." FileSource="..\bin\">
|
||||
<Directory Id="dir_sentry_0.4.9" Name="sentry-0.4.9" />
|
||||
<Directory Id="dir_sentry_0.6.0" Name="sentry-0.6.0" />
|
||||
<Directory Id="dirNode" Name="node.js" />
|
||||
<Directory Id="dirProjects" Name="Projects" />
|
||||
</Directory>
|
||||
|
|
@ -211,7 +211,7 @@
|
|||
</ComponentGroup>
|
||||
|
||||
<!-- Sentry -->
|
||||
<ComponentGroup Id="KeymanDevSentryComponents" Directory="dir_sentry_0.4.9" Source="..\..\..\common\windows\delphi\ext\sentry">
|
||||
<ComponentGroup Id="KeymanDevSentryComponents" Directory="dir_sentry_0.6.0" Source="..\..\bin\">
|
||||
<Component>
|
||||
<File Id="sentry.dll" KeyPath="yes" />
|
||||
</Component>
|
||||
|
|
|
|||
|
|
@ -15,6 +15,14 @@ export class KeysCompiler extends SectionCompiler {
|
|||
return constants.section.keys;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns just the non-touch layers.
|
||||
*/
|
||||
public hardwareLayers() {
|
||||
return this.keyboard.layers?.filter(({form}) => form !== 'touch');
|
||||
}
|
||||
|
||||
public validate() {
|
||||
let valid = true;
|
||||
|
||||
|
|
@ -37,21 +45,22 @@ export class KeysCompiler extends SectionCompiler {
|
|||
}
|
||||
}
|
||||
|
||||
// the layr compiler does more extensive validation of the layer attributes.
|
||||
|
||||
// Kmap validation
|
||||
const theLayers = this.keyboard.layers?.[0]; // TODO-LDML: handle >1 layers. #8160
|
||||
const hardwareLayers = this.hardwareLayers();
|
||||
|
||||
if(!theLayers?.layer?.length) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_MustBeAtLeastOneLayerElement());
|
||||
}
|
||||
|
||||
if(theLayers?.form == 'hardware') {
|
||||
for(let layer of theLayers?.layer) {
|
||||
valid = this.validateHardwareLayerForKmap(theLayers?.hardware, layer) && valid; // note: always validate even if previously invalid results found
|
||||
if (hardwareLayers.length >= 1) {
|
||||
// validate all errors
|
||||
for (let layers of hardwareLayers) {
|
||||
for(let layer of layers.layer) {
|
||||
valid = this.validateHardwareLayerForKmap(layers.form, layer) && valid; // note: always validate even if previously invalid results found
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO-LDML: Touch?
|
||||
}
|
||||
|
||||
// TODO-LDML: some additional validation needed here?
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
|
@ -71,15 +80,17 @@ export class KeysCompiler extends SectionCompiler {
|
|||
|
||||
// Finally, kmap
|
||||
// Use LayerMap + keys to generate compiled keys for hardware
|
||||
const theLayers = this.keyboard.layers?.[0]; // TODO-LDML: handle >1 layers. #8160
|
||||
|
||||
if(theLayers?.form == 'hardware') {
|
||||
const hardwareLayers = this.hardwareLayers();
|
||||
if (hardwareLayers.length > 1) {
|
||||
// validation should have already caught this
|
||||
throw Error(`Internal error: Expected 0 or 1 hardware layer, not ${hardwareLayers.length}`);
|
||||
} else if (hardwareLayers.length === 1) {
|
||||
const theLayers = hardwareLayers[0];
|
||||
const { form } = theLayers;
|
||||
for(let layer of theLayers.layer) {
|
||||
this.compileHardwareLayerToKmap(sections, layer, sect, theLayers.hardware);
|
||||
this.compileHardwareLayerToKmap(sections, layer, sect, form);
|
||||
}
|
||||
return sect;
|
||||
}
|
||||
// TODO-LDML: generate vkey mapping for touch-only keys
|
||||
} // else: TODO-LDML do nothing if only touch layers
|
||||
|
||||
return sect;
|
||||
}
|
||||
|
|
@ -147,7 +158,7 @@ export class KeysCompiler extends SectionCompiler {
|
|||
/**
|
||||
* TODO-LDML: from old 'keys'
|
||||
* Validate for purpose of kmap
|
||||
* @param hardware
|
||||
* @param hardware the 'form' parameter
|
||||
* @param layer
|
||||
* @returns
|
||||
*/
|
||||
|
|
@ -162,9 +173,7 @@ export class KeysCompiler extends SectionCompiler {
|
|||
|
||||
const keymap = Constants.HardwareToKeymap.get(hardware);
|
||||
if (!keymap) {
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_InvalidHardware({ hardware }));
|
||||
valid = false;
|
||||
return valid; // can't do anything else here
|
||||
throw Error(`TODO-LDML: ${hardware} not supported, see #8161`);
|
||||
}
|
||||
|
||||
const uniqueKeys = calculateUniqueKeys([...this.keyboard.keys?.key]);
|
||||
|
|
@ -202,7 +211,6 @@ export class KeysCompiler extends SectionCompiler {
|
|||
return valid;
|
||||
}
|
||||
|
||||
|
||||
private compileHardwareLayerToKmap(
|
||||
sections: GlobalSections,
|
||||
layer: LDMLKeyboard.LKLayer,
|
||||
|
|
@ -234,5 +242,4 @@ export class KeysCompiler extends SectionCompiler {
|
|||
}
|
||||
return sect;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,47 +19,41 @@ export class LayrCompiler extends SectionCompiler {
|
|||
|
||||
public validate() {
|
||||
let valid = true;
|
||||
if (!this.keyboard.layers?.[0]?.layer?.length) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_MustBeAtLeastOneLayerElement());
|
||||
}
|
||||
let totalLayerCount = 0;
|
||||
let hardwareLayers = 0;
|
||||
this.keyboard.layers.forEach((layers) => {
|
||||
const { hardware, form } = layers;
|
||||
// TODO-LDML: in the future >1 hardware layer may be allowed, check for duplicates
|
||||
// let touchLayers = 0;
|
||||
this.keyboard.layers?.forEach((layers) => {
|
||||
const { form } = layers;
|
||||
if (form === 'touch') {
|
||||
if (hardware) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_NoHardwareOnTouch({hardware}));
|
||||
}
|
||||
} else if (form === 'hardware') {
|
||||
hardwareLayers++;
|
||||
if (!hardware) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_MissingHardware());
|
||||
} else if (!constants.layr_list_hardware_map.get(hardware)) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_InvalidHardware({hardware}));
|
||||
} else if (hardwareLayers > 1) { // TODO-LDML: revisit if spec changes
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_MustHaveAtMostOneLayersElementPerForm({ form }));
|
||||
}
|
||||
// touchLayers++;
|
||||
// multiple touch layers are OK
|
||||
totalLayerCount += layers.layer?.length;
|
||||
// TODO-LDML: check that widths are distinct
|
||||
} else {
|
||||
/* c8 ignore next 7 */
|
||||
// Should not be reached due to XML validation.
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({
|
||||
errorText: `INTERNAL ERROR: Invalid XML: Invalid form="${form}" on layers element`
|
||||
}));
|
||||
// hardware
|
||||
hardwareLayers++;
|
||||
if (hardwareLayers > 1) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_ExcessHardware({form}));
|
||||
} else if (!constants.layr_list_hardware_map.get(form)) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_InvalidHardware({form}));
|
||||
}
|
||||
}
|
||||
layers.layer.forEach((layer) => {
|
||||
const { modifier, id } = layer;
|
||||
totalLayerCount++;
|
||||
if (!validModifier(modifier)) {
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_InvalidModifier({ modifier, layer: id }));
|
||||
valid = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
if (totalLayerCount === 0) { // TODO-LDML: does not validate touch layers yet
|
||||
// no layers seen anywhere
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_MustBeAtLeastOneLayerElement());
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
|
@ -67,8 +61,8 @@ export class LayrCompiler extends SectionCompiler {
|
|||
const sect = new Layr();
|
||||
|
||||
sect.lists = this.keyboard.layers.map((layers) => {
|
||||
const hardware = constants.layr_list_hardware_map.get(layers.hardware || 'touch');
|
||||
// Don't need to check 'form' because it is checked in validate
|
||||
const hardware = constants.layr_list_hardware_map.get(layers.form);
|
||||
// Already validated in validate
|
||||
const list: LayrList = {
|
||||
hardware,
|
||||
minDeviceWidth: layers.minDeviceWidth || 0,
|
||||
|
|
|
|||
|
|
@ -71,29 +71,24 @@ export class CompilerMessages {
|
|||
m(this.ERROR_KeyMissingToGapOrSwitch, `key id='${o.keyId}' must have either to=, gap=, or switch=.`);
|
||||
static ERROR_KeyMissingToGapOrSwitch = SevError | 0x0011;
|
||||
|
||||
static Error_MustHaveAtMostOneLayersElementPerForm = (o:{form: string}) => m(this.ERROR_MustHaveAtMostOneLayersElementPerForm,
|
||||
`Must have at most one layers element with form=${o.form}`);
|
||||
static ERROR_MustHaveAtMostOneLayersElementPerForm = SevError | 0x0012;
|
||||
static Error_ExcessHardware = (o:{form: string}) => m(this.ERROR_ExcessHardware,
|
||||
`layers form=${o.form}: Can only have one non-'touch' element`);
|
||||
static ERROR_ExcessHardware = SevError | 0x0012;
|
||||
|
||||
static Error_NoHardwareOnTouch = (o:{hardware: string}) => m(this.ERROR_NoHardwareOnTouch,
|
||||
`Not allowed: form=touch with hardware=${o.hardware}`);
|
||||
static ERROR_NoHardwareOnTouch = SevError | 0x0013;
|
||||
|
||||
static Error_MissingHardware = () => m(this.ERROR_MissingHardware,
|
||||
`layers form=hardware missing hardware= attribute`);
|
||||
static ERROR_MissingHardware = SevError | 0x0014;
|
||||
|
||||
static Error_InvalidHardware = (o:{hardware: string}) => m(this.ERROR_InvalidHardware,
|
||||
`layers has invalid value hardware=${o.hardware}`);
|
||||
static ERROR_InvalidHardware = SevError | 0x0015;
|
||||
static Error_InvalidHardware = (o:{form: string}) => m(this.ERROR_InvalidHardware,
|
||||
`layers has invalid value form=${o.form}`);
|
||||
/**
|
||||
* Note: may not hit this due to XML validation.
|
||||
*/
|
||||
static ERROR_InvalidHardware = SevError | 0x0013;
|
||||
|
||||
static Error_InvalidModifier = (o:{layer: string, modifier: string}) => m(this.ERROR_InvalidModifier,
|
||||
`layer has invalid modifier='${o.modifier}' on layer id=${o.layer}`);
|
||||
static ERROR_InvalidModifier = SevError | 0x0016;
|
||||
static ERROR_InvalidModifier = SevError | 0x0014;
|
||||
|
||||
static Error_MissingFlicks = (o:{flicks: string, id: string}) => m(this.ERROR_MissingFlicks,
|
||||
`key id=${o.id} refers to missing flicks=${o.flicks}`);
|
||||
static ERROR_MissingFlicks = SevError | 0x0017;
|
||||
static ERROR_MissingFlicks = SevError | 0x0015;
|
||||
|
||||
static severityName(code: number): string {
|
||||
let severity = code & CompilerErrorSeverity.Severity_Mask;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
<key id="that" to="ថា" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us" minDeviceWidth="123">
|
||||
<layers form="us" minDeviceWidth="123">
|
||||
<layer id="base">
|
||||
<row keys="hmaqtugha that" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
<row keys="Q q W w" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers form="hardware" hardware="abnt2">
|
||||
<layers form="iso">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<key id="grave" to="\u{1faa6}" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<row keys="grave" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<key id="w" to="w" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<key id="WWW" to="WWW" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base" modifier="none">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="qqq www" />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<import base="cldr" path="techpreview/keys-Zyyy-punctuation.xml"/>
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="iso">
|
||||
<layers form="iso">
|
||||
<layer id="base" modifier="none">
|
||||
<row keys="grave 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
|
||||
<row keys="q w e r t y u i o p open-square close-square" />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<import base="cldr" path="techpreview/keys-Zyyy-punctuation.xml"/>
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base" modifier="none">
|
||||
<row keys="grave 1 2 3 4 5 6 7 8 9 0 hyphen equal" />
|
||||
<row keys="q w e r t y u i o p open-square close-square backslash" />
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<keys />
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base" modifier="altR-shift"> <!-- invalid, should be "altR shift" -->
|
||||
<row keys="a b c" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
<key id="equal" to="=" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="grave one two three four five six seven eight nine ten eleven minus equal" />
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
<key id="W" to="www" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<key id="Q" /> <!-- missing to, gap, and switch -->
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping `! -->
|
||||
<row keys="Q" />
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
</flicks>
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="iso">
|
||||
<layers form="iso">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<key id="grave" to="🪦" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<row keys="foo" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
<row keys="Q q W w" />
|
||||
</layer>
|
||||
</layers>
|
||||
<layers form="hardware" hardware="abnt2">
|
||||
<layers form="iso">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="Q W" />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<key id="grave" to="🪦" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<row keys="grave" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE keyboard SYSTEM "../../../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard.dtd">
|
||||
<keyboard locale="und" conformsTo="techpreview">
|
||||
<names>
|
||||
<name value="layr-invalid-form" />
|
||||
</names>
|
||||
|
||||
<keys>
|
||||
<key id="grave" to="`" />
|
||||
<key id="one" to="1" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="stenography"> <!-- INVALID: 'stenography' is not a valid hardware type. -->
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="grave one " />
|
||||
</layer>
|
||||
</layers>
|
||||
</keyboard>
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
<key id="one" to="1" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware"> <!-- INVALID: Missing 'hardware=' attribute -->
|
||||
<layers> <!-- INVALID: Missing 'form=' attribute -->
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="grave one " />
|
||||
|
|
|
|||
|
|
@ -3,8 +3,9 @@
|
|||
<!DOCTYPE keyboard SYSTEM "../../../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard.dtd">
|
||||
<keyboard locale="mt" conformsTo="techpreview">
|
||||
<names>
|
||||
<name value="keys-minimal" />
|
||||
<name value="layr-invalid-missing-layer2" />
|
||||
</names>
|
||||
|
||||
<keys />
|
||||
<!-- invalid: need a layer! -->
|
||||
</keyboard>
|
||||
|
|
@ -11,15 +11,14 @@
|
|||
<key id="one" to="1" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layers form="us">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="grave one" />
|
||||
</layer>
|
||||
</layers>
|
||||
<!-- INVALID: can't have multiple 'hardware' layers, but
|
||||
TODO-LDML: https://github.com/unicode-org/cldr/pull/2652 would make this file valid. -->
|
||||
<layers form="hardware" hardware="iso">
|
||||
<!-- INVALID: can't have multiple 'hardware' layers -->
|
||||
<layers form="iso">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="one grave" />
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE keyboard SYSTEM "../../../../../../../resources/standards-data/ldml-keyboards/techpreview/dtd/ldmlKeyboard.dtd">
|
||||
<keyboard locale="und" conformsTo="techpreview">
|
||||
<names>
|
||||
<name value="layr-invalid-form" />
|
||||
</names>
|
||||
|
||||
<keys>
|
||||
<key id="grave" to="`" />
|
||||
<key id="one" to="1" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware" hardware="us">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="grave one" />
|
||||
</layer>
|
||||
</layers>
|
||||
<!-- INVALID: Can't have 'hardware=' when 'form=touch' -->
|
||||
<layers form="touch" hardware="iso">
|
||||
<layer id="base">
|
||||
<!-- beware: this is mapping ` and 1! -->
|
||||
<row keys="one grave" />
|
||||
</layer>
|
||||
</layers>
|
||||
</keyboard>
|
||||
|
|
@ -91,9 +91,10 @@ export function loadSectionFixture(compilerClass: typeof SectionCompiler, filena
|
|||
const reader = new LDMLKeyboardXMLSourceFileReader(callbacks);
|
||||
const source = reader.load(data);
|
||||
assert.isNotNull(source);
|
||||
assert.doesNotThrow(() => {
|
||||
reader.validate(source, callbacks.loadLdmlKeyboardSchema());
|
||||
});
|
||||
|
||||
if (!reader.validate(source, callbacks.loadLdmlKeyboardSchema())) {
|
||||
return null; // mimic kmc behavior - bail if validate fails
|
||||
}
|
||||
|
||||
const compiler = new compilerClass(source, callbacks);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,92 +10,97 @@ const K = Constants.USVirtualKeyCodes;
|
|||
|
||||
describe('keys', function () {
|
||||
this.slow(500); // 0.5 sec -- json schema validation takes a while
|
||||
|
||||
it('should compile minimal keys data', function () {
|
||||
let keys = loadSectionFixture(KeysCompiler, 'sections/keys/minimal.xml', compilerTestCallbacks) as Keys;
|
||||
assert.ok(keys);
|
||||
assert.equal(compilerTestCallbacks.messages.length, 0);
|
||||
assert.equal(keys.keys.length, 1);
|
||||
assert.equal(keys.flicks.length, 1); // there's always a 'null' flick
|
||||
assert.equal(keys.keys[0].to.value, '🪦');
|
||||
assert.equal(keys.keys[0].id.value, 'grave');
|
||||
});
|
||||
|
||||
it('should compile maximal keys data', function () {
|
||||
let keys = loadSectionFixture(KeysCompiler, 'sections/keys/maximal.xml', compilerTestCallbacks) as Keys;
|
||||
assert.ok(keys);
|
||||
assert.equal(compilerTestCallbacks.messages.length, 0);
|
||||
assert.equal(keys.keys.length, 4);
|
||||
|
||||
const [q] = keys.keys.filter(({ id }) => id.value === 'q');
|
||||
assert.ok(q);
|
||||
assert.isFalse(!!(q.flags & constants.keys_key_flags_gap));
|
||||
assert.equal(q.width, 32, 'q\'s width'); // ceil(3.14159 * 10.0)
|
||||
assert.equal(q.flicks, 'flick0'); // note this is a string, not a StrsItem
|
||||
assert.equal(q.longPress.toString(), 'á é í');
|
||||
assert.equal(q.longPressDefault.value, 'é');
|
||||
assert.equal(q.multiTap.toString(), 'ä ë ï');
|
||||
|
||||
const [flick0] = keys.flicks.filter(({ id }) => id.value === 'flick0');
|
||||
assert.ok(flick0);
|
||||
assert.equal(flick0.flicks.length, 2);
|
||||
|
||||
const [flick0_nw_se] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('nw se'.split(' ')));
|
||||
assert.ok(flick0_nw_se);
|
||||
assert.equal(flick0_nw_se.to?.value, 'ç');
|
||||
|
||||
const [flick0_ne_sw] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('ne sw'.split(' ')));
|
||||
assert.ok(flick0_ne_sw);
|
||||
assert.equal(flick0_ne_sw.to?.value, 'ê');
|
||||
});
|
||||
|
||||
it('should compile escaped keys data', function () {
|
||||
let keys = loadSectionFixture(KeysCompiler, 'sections/keys/escaped.xml', compilerTestCallbacks) as Keys;
|
||||
assert.ok(keys);
|
||||
assert.equal(compilerTestCallbacks.messages.length, 0);
|
||||
assert.equal(keys.keys.length, 4);
|
||||
|
||||
const [q] = keys.keys.filter(({ id }) => id.value === 'q');
|
||||
assert.ok(q);
|
||||
assert.isFalse(!!(q.flags & constants.keys_key_flags_gap));
|
||||
assert.equal(q.width, 32); // ceil(3.1 * 10)
|
||||
assert.equal(q.flicks, 'flick0'); // note this is a string, not a StrsItem
|
||||
assert.equal(q.longPress.toString(), 'á é í');
|
||||
assert.equal(q.longPressDefault.value, 'é');
|
||||
assert.equal(q.multiTap.toString(), 'ä ë ï');
|
||||
|
||||
const [flick0] = keys.flicks.filter(({ id }) => id.value === 'flick0');
|
||||
assert.ok(flick0);
|
||||
assert.equal(flick0.flicks.length, 2);
|
||||
|
||||
const [flick0_nw_se] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('nw se'.split(' ')));
|
||||
assert.ok(flick0_nw_se);
|
||||
assert.equal(flick0_nw_se.to?.value, 'ç');
|
||||
|
||||
const [flick0_ne_sw] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('ne sw'.split(' ')));
|
||||
assert.ok(flick0_ne_sw);
|
||||
assert.equal(flick0_ne_sw.to?.value, 'ế');
|
||||
});
|
||||
|
||||
|
||||
it('should accept layouts with gap/switch keys', function () {
|
||||
let keys = loadSectionFixture(KeysCompiler, 'sections/keys/gap-switch.xml', compilerTestCallbacks) as Keys;
|
||||
assert.ok(keys);
|
||||
assert.equal(compilerTestCallbacks.messages.length, 0);
|
||||
assert.equal(keys.keys.length, 4);
|
||||
|
||||
const [Qgap] = keys.keys.filter(({ id }) => id.value === 'Q');
|
||||
assert.ok(Qgap);
|
||||
assert.isTrue(!!(Qgap.flags & constants.keys_key_flags_gap), 'Q’s gap=');
|
||||
|
||||
const [Wshift] = keys.keys.filter(({ id }) => id.value === 'W');
|
||||
assert.isNotNull(Wshift);
|
||||
assert.isFalse(!!(Wshift.flags & constants.keys_key_flags_gap));
|
||||
assert.equal(Wshift.switch.value, 'shift');
|
||||
|
||||
});
|
||||
|
||||
testCompilationCases(KeysCompiler, [
|
||||
{
|
||||
// should compile minimal keys data
|
||||
subpath: 'sections/keys/minimal.xml',
|
||||
callback(sect) {
|
||||
const keys = <Keys> sect;
|
||||
assert.ok(keys);
|
||||
assert.equal(compilerTestCallbacks.messages.length, 0);
|
||||
assert.equal(keys.keys.length, 1);
|
||||
assert.equal(keys.flicks.length, 1); // there's always a 'null' flick
|
||||
assert.equal(keys.keys[0].to.value, String.fromCodePoint(0x1FAA6));
|
||||
assert.equal(keys.keys[0].id.value, 'grave');
|
||||
},
|
||||
},
|
||||
{
|
||||
subpath: 'sections/keys/maximal.xml',
|
||||
callback(sect) {
|
||||
const keys = <Keys> sect;
|
||||
assert.ok(keys);
|
||||
assert.equal(compilerTestCallbacks.messages.length, 0);
|
||||
assert.equal(keys.keys.length, 4);
|
||||
|
||||
const [q] = keys.keys.filter(({ id }) => id.value === 'q');
|
||||
assert.ok(q);
|
||||
assert.isFalse(!!(q.flags & constants.keys_key_flags_gap));
|
||||
assert.equal(q.width, 32, 'q\'s width'); // ceil(3.14159 * 10.0)
|
||||
assert.equal(q.flicks, 'flick0'); // note this is a string, not a StrsItem
|
||||
assert.equal(q.longPress.toString(), 'á é í');
|
||||
assert.equal(q.longPressDefault.value, 'é');
|
||||
assert.equal(q.multiTap.toString(), 'ä ë ï');
|
||||
|
||||
const [flick0] = keys.flicks.filter(({ id }) => id.value === 'flick0');
|
||||
assert.ok(flick0);
|
||||
assert.equal(flick0.flicks.length, 2);
|
||||
|
||||
const [flick0_nw_se] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('nw se'.split(' ')));
|
||||
assert.ok(flick0_nw_se);
|
||||
assert.equal(flick0_nw_se.to?.value, 'ç');
|
||||
|
||||
const [flick0_ne_sw] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('ne sw'.split(' ')));
|
||||
assert.ok(flick0_ne_sw);
|
||||
assert.equal(flick0_ne_sw.to?.value, 'ê');
|
||||
},
|
||||
},
|
||||
{
|
||||
subpath: 'sections/keys/escaped.xml',
|
||||
callback(sect) {
|
||||
const keys = <Keys> sect;
|
||||
assert.ok(keys);
|
||||
assert.equal(compilerTestCallbacks.messages.length, 0);
|
||||
assert.equal(keys.keys.length, 4);
|
||||
|
||||
const [q] = keys.keys.filter(({ id }) => id.value === 'q');
|
||||
assert.ok(q);
|
||||
assert.isFalse(!!(q.flags & constants.keys_key_flags_gap));
|
||||
assert.equal(q.width, 32); // ceil(3.1 * 10)
|
||||
assert.equal(q.flicks, 'flick0'); // note this is a string, not a StrsItem
|
||||
assert.equal(q.longPress.toString(), 'á é í');
|
||||
assert.equal(q.longPressDefault.value, 'é');
|
||||
assert.equal(q.multiTap.toString(), 'ä ë ï');
|
||||
|
||||
const [flick0] = keys.flicks.filter(({ id }) => id.value === 'flick0');
|
||||
assert.ok(flick0);
|
||||
assert.equal(flick0.flicks.length, 2);
|
||||
|
||||
const [flick0_nw_se] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('nw se'.split(' ')));
|
||||
assert.ok(flick0_nw_se);
|
||||
assert.equal(flick0_nw_se.to?.value, 'ç');
|
||||
|
||||
const [flick0_ne_sw] = flick0.flicks.filter(({ directions }) => directions && directions.isEqual('ne sw'.split(' ')));
|
||||
assert.ok(flick0_ne_sw);
|
||||
assert.equal(flick0_ne_sw.to?.value, 'ế');
|
||||
},
|
||||
},
|
||||
{
|
||||
subpath: 'sections/keys/gap-switch.xml',
|
||||
callback(sect) {
|
||||
const keys = <Keys> sect;
|
||||
assert.equal(compilerTestCallbacks.messages.length, 0);
|
||||
assert.equal(keys.keys.length, 4);
|
||||
|
||||
const [Qgap] = keys.keys.filter(({ id }) => id.value === 'Q');
|
||||
assert.ok(Qgap);
|
||||
assert.isTrue(!!(Qgap.flags & constants.keys_key_flags_gap), 'Q’s gap=');
|
||||
|
||||
const [Wshift] = keys.keys.filter(({ id }) => id.value === 'W');
|
||||
assert.isNotNull(Wshift);
|
||||
assert.isFalse(!!(Wshift.flags & constants.keys_key_flags_gap));
|
||||
assert.equal(Wshift.switch.value, 'shift');
|
||||
},
|
||||
},
|
||||
{
|
||||
subpath: 'sections/keys/escaped2.xml',
|
||||
callback: (keys, subpath, callbacks) => {
|
||||
|
|
@ -212,14 +217,6 @@ describe('keys.kmap', function () {
|
|||
},
|
||||
]);
|
||||
|
||||
it('should reject structurally invalid layers', function() {
|
||||
let keys = loadSectionFixture(KeysCompiler, 'sections/keys/invalid-missing-layer.xml', compilerTestCallbacks) as Keys;
|
||||
assert.isNull(keys);
|
||||
assert.equal(compilerTestCallbacks.messages.length, 1);
|
||||
|
||||
assert.deepEqual(compilerTestCallbacks.messages[0], CompilerMessages.Error_MustBeAtLeastOneLayerElement());
|
||||
});
|
||||
|
||||
it('should reject layouts with too many hardware rows', function() {
|
||||
let keys = loadSectionFixture(KeysCompiler, 'sections/keys/invalid-hardware-too-many-rows.xml', compilerTestCallbacks) as Keys;
|
||||
assert.isNull(keys);
|
||||
|
|
|
|||
|
|
@ -43,66 +43,57 @@ describe('layr', function () {
|
|||
assert.equal(row0.keys[0]?.value, 'grave');
|
||||
});
|
||||
|
||||
// reuse keys maximal
|
||||
it('should compile maximal keys data', function () {
|
||||
let layr = loadSectionFixture(LayrCompiler, 'sections/keys/maximal.xml', compilerTestCallbacks) as Layr;
|
||||
assert.ok(layr);
|
||||
assert.equal(compilerTestCallbacks.messages.length, 0);
|
||||
|
||||
assert.equal(layr.lists?.length, 2);
|
||||
|
||||
const listHardware = layr.lists.find(v => v.hardware === constants.layr_list_hardware_abnt2);
|
||||
assert.ok(listHardware);
|
||||
assert.equal(listHardware.minDeviceWidth, 0);
|
||||
assert.equal(listHardware.layers.length, 2);
|
||||
const hardware0 = listHardware.layers[0];
|
||||
assert.ok(hardware0);
|
||||
assert.equal(hardware0.id.value, 'base');
|
||||
assert.equal(hardware0.mod, constants.keys_mod_none);
|
||||
const hardware0row0 = hardware0.rows[0];
|
||||
assert.ok(hardware0row0);
|
||||
assert.equal(hardware0row0.keys.length, 2);
|
||||
allKeysOk(hardware0row0,'Q W', 'hardware0row0');
|
||||
const hardware1 = listHardware.layers[1];
|
||||
assert.ok(hardware1);
|
||||
assert.equal(hardware1.rows.length, 1);
|
||||
assert.equal(hardware1.id.value, 'shift');
|
||||
assert.equal(hardware1.mod, constants.keys_mod_shift);
|
||||
const hardware1row0 = hardware1.rows[0];
|
||||
assert.ok(hardware1row0);
|
||||
assert.equal(hardware1row0.keys.length, 2);
|
||||
allKeysOk(hardware1row0,'q w', 'hardware1row0');
|
||||
|
||||
const listTouch = layr.lists.find(v => v.hardware === constants.layr_list_hardware_touch);
|
||||
assert.ok(listTouch);
|
||||
assert.equal(listTouch.minDeviceWidth, 300);
|
||||
assert.equal(listTouch.layers.length, 1);
|
||||
const touch0 = listTouch.layers[0];
|
||||
assert.ok(touch0);
|
||||
assert.equal(touch0.rows.length, 1);
|
||||
assert.equal(touch0.id.value, 'base');
|
||||
assert.equal(touch0.mod, constants.keys_mod_none);
|
||||
const touch0row0 = touch0.rows[0];
|
||||
assert.ok(touch0row0);
|
||||
assert.equal(touch0row0.keys.length, 4);
|
||||
allKeysOk(touch0row0,'Q q W w', 'touch0row0');
|
||||
});
|
||||
testCompilationCases(LayrCompiler, [
|
||||
{
|
||||
subpath: 'sections/layr/invalid-invalid-hardware.xml',
|
||||
errors: [CompilerMessages.Error_InvalidHardware({hardware: 'stenography'})],
|
||||
subpath: 'sections/keys/maximal.xml',
|
||||
callback(sect) {
|
||||
const layr = <Layr> sect;
|
||||
assert.equal(layr.lists?.length, 2);
|
||||
|
||||
const listHardware = layr.lists.find(v => v.hardware === constants.layr_list_hardware_iso);
|
||||
assert.ok(listHardware);
|
||||
assert.equal(listHardware.minDeviceWidth, 0);
|
||||
assert.equal(listHardware.layers.length, 2);
|
||||
const hardware0 = listHardware.layers[0];
|
||||
assert.ok(hardware0);
|
||||
assert.equal(hardware0.id.value, 'base');
|
||||
assert.equal(hardware0.mod, constants.keys_mod_none);
|
||||
const hardware0row0 = hardware0.rows[0];
|
||||
assert.ok(hardware0row0);
|
||||
assert.equal(hardware0row0.keys.length, 2);
|
||||
allKeysOk(hardware0row0,'Q W', 'hardware0row0');
|
||||
const hardware1 = listHardware.layers[1];
|
||||
assert.ok(hardware1);
|
||||
assert.equal(hardware1.rows.length, 1);
|
||||
assert.equal(hardware1.id.value, 'shift');
|
||||
assert.equal(hardware1.mod, constants.keys_mod_shift);
|
||||
const hardware1row0 = hardware1.rows[0];
|
||||
assert.ok(hardware1row0);
|
||||
assert.equal(hardware1row0.keys.length, 2);
|
||||
allKeysOk(hardware1row0,'q w', 'hardware1row0');
|
||||
|
||||
const listTouch = layr.lists.find(v => v.hardware === constants.layr_list_hardware_touch);
|
||||
assert.ok(listTouch);
|
||||
assert.equal(listTouch.minDeviceWidth, 300);
|
||||
assert.equal(listTouch.layers.length, 1);
|
||||
const touch0 = listTouch.layers[0];
|
||||
assert.ok(touch0);
|
||||
assert.equal(touch0.rows.length, 1);
|
||||
assert.equal(touch0.id.value, 'base');
|
||||
assert.equal(touch0.mod, constants.keys_mod_none);
|
||||
const touch0row0 = touch0.rows[0];
|
||||
assert.ok(touch0row0);
|
||||
assert.equal(touch0row0.keys.length, 4);
|
||||
allKeysOk(touch0row0,'Q q W w', 'touch0row0');
|
||||
},
|
||||
},
|
||||
{
|
||||
subpath: 'sections/layr/invalid-missing-hardware.xml',
|
||||
errors: [CompilerMessages.Error_MissingHardware()],
|
||||
errors: [],
|
||||
},
|
||||
{
|
||||
subpath: 'sections/layr/invalid-multi-hardware.xml',
|
||||
errors: [CompilerMessages.Error_MustHaveAtMostOneLayersElementPerForm({ form: 'hardware' })],
|
||||
},
|
||||
{
|
||||
subpath: 'sections/layr/invalid-touch-hardware.xml',
|
||||
errors: [CompilerMessages.Error_NoHardwareOnTouch({ hardware: 'iso' })],
|
||||
errors: [CompilerMessages.Error_ExcessHardware({ form: 'iso' })],
|
||||
},
|
||||
{
|
||||
subpath: 'sections/layr/invalid-invalid-form.xml',
|
||||
|
|
@ -110,11 +101,19 @@ describe('layr', function () {
|
|||
instancePath: '/keyboard/layers/0/form',
|
||||
keyword: 'enum',
|
||||
message: 'must be equal to one of the allowed values',
|
||||
params: `allowedValues="hardware,touch"`}),],
|
||||
params: `allowedValues="touch,us,iso,jis,abnt2"`}),],
|
||||
},
|
||||
{
|
||||
// missing layer element
|
||||
subpath: 'sections/layr/invalid-missing-layer.xml',
|
||||
errors: [CompilerMessages.Error_MustBeAtLeastOneLayerElement()],
|
||||
},
|
||||
{
|
||||
// missing layers element completely
|
||||
subpath: 'sections/layr/invalid-missing-layer.xml',
|
||||
errors: [
|
||||
CompilerMessages.Error_MustBeAtLeastOneLayerElement(),
|
||||
],
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@
|
|||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
"bundle": "npm run bundle-kmc && npm run bundle-kmlmc && npm run bundle-kmlmi && npm run bundle-kmlmp",
|
||||
"bundle-kmc": "esbuild build/src/kmc.js --bundle --platform=node > build/cjs-src/kmc.cjs",
|
||||
"bundle-kmlmc": "esbuild build/src/kmlmc.js --bundle --platform=node > build/cjs-src/kmlmc.cjs",
|
||||
"bundle-kmlmi": "esbuild build/src/kmlmi.js --bundle --platform=node > build/cjs-src/kmlmi.cjs",
|
||||
"bundle-kmlmp": "esbuild build/src/kmlmp.js --bundle --platform=node > build/cjs-src/kmlmp.cjs",
|
||||
"bundle-kmc": "esbuild build/src/kmc.js --bundle --platform=node --target=es2022 > build/cjs-src/kmc.cjs",
|
||||
"bundle-kmlmc": "esbuild build/src/kmlmc.js --bundle --platform=node --target=es2022 > build/cjs-src/kmlmc.cjs",
|
||||
"bundle-kmlmi": "esbuild build/src/kmlmi.js --bundle --platform=node --target=es2022 > build/cjs-src/kmlmi.cjs",
|
||||
"bundle-kmlmp": "esbuild build/src/kmlmp.js --bundle --platform=node --target=es2022 > build/cjs-src/kmlmp.cjs",
|
||||
"test": "cd test && tsc -b && cd .. && c8 --reporter=lcov --reporter=text mocha",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ clean: def-clean
|
|||
signcode:
|
||||
$(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\tike.exe
|
||||
$(SIGNCODE) /d "Keyman Core" $(DEVELOPER_PROGRAM)\kmnkbp0-0.dll
|
||||
# Sign the Sentry executables and libraries here
|
||||
$(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\sentry.dll
|
||||
$(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\sentry.x64.dll
|
||||
$(SIGNCODE) /d "Keyman Developer" $(DEVELOPER_PROGRAM)\crashpad_handler.exe
|
||||
|
||||
wrap-symbols:
|
||||
$(SYMSTORE) $(DEVELOPER_PROGRAM)\tike.exe /t keyman-developer
|
||||
|
|
|
|||
|
|
@ -1,18 +1,41 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "shell",
|
||||
"label": "core: clean",
|
||||
"command": "${workspaceFolder}/core/build.sh",
|
||||
"args": [
|
||||
"--debug",
|
||||
"clean"
|
||||
],
|
||||
"group": "build",
|
||||
"detail": "clean core"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"label": "core: configure",
|
||||
"command": "${workspaceFolder}/core/build.sh",
|
||||
"args": [
|
||||
"--debug",
|
||||
"--no-tests",
|
||||
"configure:arch"
|
||||
],
|
||||
"presentation": {
|
||||
"clear": true
|
||||
},
|
||||
"group": "build",
|
||||
"detail": "configure core"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"label": "core: build",
|
||||
"command": "${workspaceFolder}/core/build.sh",
|
||||
"args": [
|
||||
"configure:arch",
|
||||
"build:arch",
|
||||
"--debug"
|
||||
"--debug",
|
||||
"--no-tests",
|
||||
"build:arch"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/core"
|
||||
},
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "cpp",
|
||||
|
|
@ -49,70 +72,6 @@
|
|||
"group": "build",
|
||||
"detail": "build core"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"label": "core: configure",
|
||||
"command": "${workspaceFolder}/core/build.sh",
|
||||
"args": [ "--debug", "configure",
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/core",
|
||||
},
|
||||
"presentation": {
|
||||
"clear": true
|
||||
},
|
||||
"group": "build",
|
||||
"detail": "configure core"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"label": "core: configure+build",
|
||||
"command": "${workspaceFolder}/core/build.sh",
|
||||
"args": [
|
||||
"--debug", "configure", "build", "tests"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/core"
|
||||
},
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "cpp",
|
||||
"source": "compiler",
|
||||
"fileLocation": [
|
||||
"relative",
|
||||
"${workspaceFolder}/core/"
|
||||
],
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^../../../(.*):(\\d+):(\\d+):\\s+(warning|error|note):\\s+(.*)(\\s+\\[.*\\])?$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"owner": "cpp",
|
||||
"source": "linker",
|
||||
"fileLocation": "absolute",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(/usr/bin/ld:\\s+)?(.*):(\\d+):\\s+(.*)$",
|
||||
"file": 2,
|
||||
"location": 3,
|
||||
"message": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
"presentation": {
|
||||
"clear": true
|
||||
},
|
||||
"group": "build",
|
||||
"detail": "build core"
|
||||
},
|
||||
{
|
||||
"type": "shell",
|
||||
"label": "core: test",
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@
|
|||
"kmp-error-missing-resource" = "Package ǝni adiyaka kibod ko dictionary namtǝ gǝ yuwɛ wɛ.";
|
||||
|
||||
/* Error installing a Keyman package with a version of Keyman that does not support it */
|
||||
"kmp-error-unsupported-keyman-version" = "This package requires a newer version of Keyman.";
|
||||
"kmp-error-unsupported-keyman-version" = "Pakeji ǝni ada kǝthlǝr aka jiliyir Keyman nam bǝlin.";
|
||||
|
||||
/* Error opening a Keyman package - cannot parse contents */
|
||||
"kmp-error-no-metadata" = "Package ǝni ndadi tsati tǝni kal kal wɛ - suyere nam akwa ndadi zǝndi wɛ.";
|
||||
|
|
|
|||
|
|
@ -1,3 +1,17 @@
|
|||
keyman (16.0.139-2) unstable; urgency=medium
|
||||
|
||||
* Don't build on s390x because Keyman doesn't work on big-endian architectures
|
||||
(upstream bug https://github.com/keymanapp/keyman/issues/5111)
|
||||
|
||||
-- Eberhard Beilharz <eb1@sil.org> Mon, 20 Mar 2023 19:54:44 +0100
|
||||
|
||||
keyman (16.0.139-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release.
|
||||
* Re-release to Debian
|
||||
|
||||
-- Eberhard Beilharz <eb1@sil.org> Thu, 16 Mar 2023 08:59:04 +0100
|
||||
|
||||
keyman (16.0.138-4) unstable; urgency=medium
|
||||
|
||||
* Team upload
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ Description: Keyman for Linux configuration
|
|||
information about Keyman keyboard packages.
|
||||
|
||||
Package: libkmnkbp-dev
|
||||
Architecture: any
|
||||
Architecture: amd64 arm64 armel armhf i386 mipsel mips64el ppc64el riscv64
|
||||
Section: libdevel
|
||||
Depends:
|
||||
libkmnkbp0-0 (= ${binary:Version}),
|
||||
|
|
@ -129,7 +129,7 @@ Description: Development files for Keyman keyboard processing library
|
|||
This package contains development headers and libraries.
|
||||
|
||||
Package: libkmnkbp0-0
|
||||
Architecture: any
|
||||
Architecture: amd64 arm64 armel armhf i386 mipsel mips64el ppc64el riscv64
|
||||
Section: libs
|
||||
Pre-Depends:
|
||||
${misc:Pre-Depends},
|
||||
|
|
@ -155,7 +155,7 @@ Description: Keyman keyboard processing library
|
|||
and applies rules from compiled Keyman keyboard files.
|
||||
|
||||
Package: ibus-keyman
|
||||
Architecture: any
|
||||
Architecture: amd64 arm64 armel armhf i386 mipsel mips64el ppc64el riscv64
|
||||
Depends:
|
||||
ibus (>= 1.3.7),
|
||||
sudo,
|
||||
|
|
|
|||
|
|
@ -18,11 +18,8 @@ builder_describe \
|
|||
"build" \
|
||||
"test" \
|
||||
"install install artifacts" \
|
||||
"uninstall uninstall artifacts"
|
||||
|
||||
# We can't yet depend on core until it moved to the new build.sh syntax
|
||||
# (currently it doesn't know some parameters that we're passing)
|
||||
# "@/core configure build"
|
||||
"uninstall uninstall artifacts" \
|
||||
"@/core:arch"
|
||||
|
||||
builder_parse "$@"
|
||||
|
||||
|
|
|
|||
|
|
@ -281,8 +281,11 @@ class InstallKmp():
|
|||
|
||||
|
||||
def extract_kmp(kmpfile, directory):
|
||||
with zipfile.ZipFile(kmpfile, "r") as zip_ref:
|
||||
zip_ref.extractall(directory)
|
||||
try:
|
||||
with zipfile.ZipFile(kmpfile, "r") as zip_ref:
|
||||
zip_ref.extractall(directory)
|
||||
except zipfile.BadZipFile as e:
|
||||
raise InstallError(InstallStatus.Abort, e)
|
||||
|
||||
|
||||
def process_keyboard_data(keyboardID, packageDir) -> None:
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class InstallKmpWindow(Gtk.Dialog):
|
|||
|
||||
def __init__(self, kmpfile, viewkmp=None, language=None):
|
||||
logging.debug("InstallKmpWindow: kmpfile: %s", kmpfile)
|
||||
self.is_error = False
|
||||
self.kmpfile = kmpfile
|
||||
self.viewwindow = viewkmp
|
||||
self.accelerators = None
|
||||
|
|
@ -67,7 +68,13 @@ class InstallKmpWindow(Gtk.Dialog):
|
|||
mainhbox = Gtk.Box()
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
extract_kmp(kmpfile, tmpdirname)
|
||||
try:
|
||||
extract_kmp(kmpfile, tmpdirname)
|
||||
except InstallError as e:
|
||||
self._handle_install_error(e, kmpfile)
|
||||
self.is_error = True
|
||||
return
|
||||
|
||||
info, system, options, keyboards, files = get_metadata(tmpdirname)
|
||||
if not keyboards:
|
||||
# Likely not a keyboard .kmp file
|
||||
|
|
@ -357,27 +364,30 @@ class InstallKmpWindow(Gtk.Dialog):
|
|||
dialog.run()
|
||||
dialog.destroy()
|
||||
except InstallError as e:
|
||||
if e.status == InstallStatus.Abort:
|
||||
message = _("Keyboard {name} could not be installed.").format(name=self.kbname) \
|
||||
+ "\n\n" + _("Error Message:") + "\n %s" % (e.message)
|
||||
logging.error(message)
|
||||
message_type = Gtk.MessageType.ERROR
|
||||
else:
|
||||
message = _("Keyboard {name} could not be installed.").format(name=self.kbname) \
|
||||
+ "\n\n" + _("Warning Message:") + "\n %s" % (e.message)
|
||||
logging.warning(message)
|
||||
message_type = Gtk.MessageType.WARNING
|
||||
dialog = Gtk.MessageDialog(
|
||||
self, 0, message_type,
|
||||
Gtk.ButtonsType.OK, message)
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
self._handle_install_error(e, self.kbname)
|
||||
self.close()
|
||||
|
||||
def on_cancel_clicked(self, button):
|
||||
logging.info("Cancel install keyboard")
|
||||
self.response(Gtk.ResponseType.CANCEL)
|
||||
|
||||
def _handle_install_error(self, e, kbname):
|
||||
if e.status == InstallStatus.Abort:
|
||||
message = _("Keyboard {name} could not be installed.").format(name=kbname) \
|
||||
+ "\n\n" + _("Error Message:") + "\n %s" % (e.message)
|
||||
logging.error(message)
|
||||
message_type = Gtk.MessageType.ERROR
|
||||
else:
|
||||
message = _("Keyboard {name} could not be installed.").format(name=kbname) \
|
||||
+ "\n\n" + _("Warning Message:") + "\n %s" % (e.message)
|
||||
logging.warning(message)
|
||||
message_type = Gtk.MessageType.WARNING
|
||||
dialog = Gtk.MessageDialog(
|
||||
self, 0, message_type,
|
||||
Gtk.ButtonsType.OK, message)
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
|
||||
def main(argv):
|
||||
if len(sys.argv) != 2:
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ class ViewInstalledWindowBase(Gtk.Window):
|
|||
|
||||
def install_file(self, kmpfile, language=None):
|
||||
installDlg = InstallKmpWindow(kmpfile, viewkmp=self, language=language)
|
||||
if installDlg.is_error:
|
||||
return Gtk.ResponseType.CANCEL
|
||||
result = installDlg.run()
|
||||
installDlg.destroy()
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ msgstr ""
|
|||
"Project-Id-Version: keyman\n"
|
||||
"Report-Msgid-Bugs-To: <support@keyman.com>\n"
|
||||
"POT-Creation-Date: 2020-08-19 19:17+0200\n"
|
||||
"PO-Revision-Date: 2022-10-25 04:29\n"
|
||||
"PO-Revision-Date: 2023-03-16 04:24\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Kibaku\n"
|
||||
"Language: ckl_NG\n"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
/* button text to close the About window */
|
||||
"Kab-Up-fYH.title" = "Harti";
|
||||
|
||||
/* text of link to the license agreement */
|
||||
"hYC-Bx-aoP.title" = "Lasis nǝr ʻYa suyere";
|
||||
|
||||
/* button text to open Configuration window */
|
||||
"vkh-b5-vO7.title" = "Whalir ʻya suyere wato Konfigareshon...";
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
/* Checkbox text to always show on-screen keyboard */
|
||||
"4UX-80-0Vo.title" = "Ka dzǝ tsanti ata skrin nǝr kibod kǝlara saka";
|
||||
|
||||
/* Checkbox text to enable verbose Console logging */
|
||||
"7J6-zy-R20.title" = "Kǝthlǝr aka verbos Konsol akwa sakar lukwa layir sambar";
|
||||
|
||||
/* Support button text */
|
||||
"93O-x6-RLF.label" = "Sǝr dlarɛ";
|
||||
|
||||
/* Download Keyboard button text */
|
||||
"CTw-kf-WNS.title" = "Sǝgǝl aka Kibod...";
|
||||
|
||||
/* Keyman Configuration window title */
|
||||
"F0z-JX-Cv5.title" = "Su namtǝ ndǝ ɗǝmɦɛ ŋǝr Keyman";
|
||||
|
||||
/* button text to go back to previous page */
|
||||
"JOK-JV-n8w.title" = "Mwari tubwa hi";
|
||||
|
||||
/* Keyboards button text */
|
||||
"MPN-9N-wWc.label" = "Kibodyere";
|
||||
|
||||
/* text to explain verbose Console logging option */
|
||||
"MrI-GM-7d6.title" = "Ma saka namtǝ verbos logging ada kǝthlǝr, Keyman ata kǝthlǝr akwa whalɛ namtǝ sǝr dlarǝrniyere ata ʻya ka zǝndiba damicinyere nam adai. Ndata mǝzi kǝthlǝr aka Program nǝr Klnsol ka ndǝ wuti labaryere nǝr Keyman nam akwa Klnsole, ka ndǝ tsanti shanga labaryere nam akwa kuta Keyman. Ndata mǝzi hyanba shanga labaryere ǝni alukwa dlarǝr Keyman ma ndɛ ada bara.";
|
||||
|
||||
/* button text to move forward to the next page */
|
||||
"eXr-8V-h1g.title" = "Alǝkǝma";
|
||||
|
||||
/* button text to return to the home help page */
|
||||
"fXS-aC-CMH.title" = "Livi ko alivi";
|
||||
|
||||
/* Options button text */
|
||||
"frd-No-seV.label" = "Sǝr tsarba ka ʻyayere";
|
||||
|
||||
/* Installed Keyboard column heading */
|
||||
"jex-Nd-Qzg.headerCell.title" = "Hau ko vu kibod";
|
||||
|
||||
/* Verbose logging tooltip text */
|
||||
"uWx-3J-U0D.ibShadowedToolTip" = "Hyanti su ǝni ma gaka damicinidzi laka aka kibod laka ko aka kǝlara Keyman laka.";
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/* Package Information window title */
|
||||
"F0z-JX-Cv5.title" = "Tsabtǝdzǝr Sǝr Zǝndiyere";
|
||||
|
||||
/* button text to show Details */
|
||||
"Fvy-XJ-s38.label" = "Suyere nam akuyaka nji zǝndi su ata kǝryere";
|
||||
|
||||
/* button text to show Read Me */
|
||||
"waA-IW-Qyn.label" = "Kǝratu Tǝra";
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
/* Keyboard Help window title */
|
||||
"F0z-JX-Cv5.title" = "Dlarɛ nǝr Kibod";
|
||||
|
||||
/* OK button text */
|
||||
"Zla-QF-m0K.title" = "Kal kal";
|
||||
|
||||
/* Print button text */
|
||||
"fYY-Uj-y4S.title" = "Print...";
|
||||
71
mac/Keyman4MacIM/Keyman4MacIM/ckl.lproj/Localizable.strings
Normal file
71
mac/Keyman4MacIM/Keyman4MacIM/ckl.lproj/Localizable.strings
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/* Message displayed to confirm delete of the Keyman keyboard selected by the user from the keyboard list */
|
||||
"message-confirm-delete-keyboard" = "Gada bara kǝlba kǝlba ka ɗalba kibod '%@' ya?";
|
||||
|
||||
/* Delete keyboard confirmation info indicating that the delete action cannot be undone */
|
||||
"info-cannot-undo-delete-keyboard" = "Gadiyata mǝzi ka pali su namtǝ gǝ kurta ʻyati wɛ.";
|
||||
|
||||
/* Button text to cancel delete of the specified installed Keyman keyboard */
|
||||
"button-cancel-delete-keyboard" = "Kansǝl";
|
||||
|
||||
/* Button text to confirm delete of the specified installed Keyman keyboard */
|
||||
"button-delete-keyboard" = "Kǝlba ka ɗalba";
|
||||
|
||||
/* Message displayed to confirm installation of a keyboard from the specifed .kmp file double-clicked by the user */
|
||||
"message-confirm-install-keyboard" = "Vu Keyman kibod?";
|
||||
|
||||
/* Message displayed to confirm installation of a keyboard from the specifed .kmp file double-clicked by the user */
|
||||
"info-install-keyboard-filename" = "Gada bara ka Keyman vu alagɛ kibod nam akwa kuta fail ya '%@'?";
|
||||
|
||||
/* Button text to cancel installation of the double-clicked Keyman file */
|
||||
"button-cancel-install-keyboard" = "Kansǝl";
|
||||
|
||||
/* Button text to confirm installation of the double-clicked Keyman file */
|
||||
"button-install-keyboard" = "Vu ko hau";
|
||||
|
||||
/* Message displayed to inform user that .kmp file could not be read/unzipped */
|
||||
"message-keyboard-file-unreadable" = "Ta mǝzi ka karatɛ fail nǝr Keyman wɛ '%@'.";
|
||||
|
||||
/* Message displayed in Configuration window when keyboard cannot be loaded */
|
||||
"message-error-loading-keyboard" = "(aibe akwa vu kibod)";
|
||||
|
||||
/* Message displayed in Configuration window when keyboard metadata cannot be loaded */
|
||||
"message-error-unknown-metadata" = "ndǝta zǝndi suyari wɛ";
|
||||
|
||||
/* Button text to acknowledge that .kmp file could not be read */
|
||||
"button-keyboard-file-unreadable" = "Kal kal";
|
||||
|
||||
/* label text to identify Keyman version */
|
||||
"version-label-text" = "Jiliyari %@";
|
||||
|
||||
/* Status text for keyboard downloading */
|
||||
"message-keyboard-downloading" = "Ata kǝra sǝgǝl akǝr...";
|
||||
|
||||
/* Status text for keyboard download complete */
|
||||
"message-keyboard-download-complete" = "Hau ko sǝgǝl aka suyari kǝ kuri.";
|
||||
|
||||
/* Button text to cancel downloading keyboard */
|
||||
"button-cancel-downloading" = "Kansǝl";
|
||||
|
||||
/* Button text keyboard download complete */
|
||||
"button-download-complete" = "Kurai ʻyatidzi";
|
||||
|
||||
/* keyboards label in the Package Information window */
|
||||
"keyboards-label" = "Kibodyere:";
|
||||
|
||||
/* fonts label in the Package Information window */
|
||||
"fonts-label" = "Fontyere:";
|
||||
|
||||
/* package version label in the Package Information window */
|
||||
"package-version-label" = "Jiliyir tsabtǝdzǝr failyere nam ada mǝrti su apa kibod namtǝ ndɛ ada na njǝr kuthlǝr aka kamputa:";
|
||||
|
||||
/* author label in the Package Information window */
|
||||
"author-label" = "Ndǝ nam tara ʻyati sari:";
|
||||
|
||||
/* website label in the Package Information window */
|
||||
"website-label" = "Websait:";
|
||||
|
||||
/* copyright label in the Package Information window */
|
||||
"copyright-label" = "Kopirait:";
|
||||
|
||||
/* message displayed to alert user to need grant accessibility permission */
|
||||
"privacy-alert-text" = "To function properly, Keyman requires accessibility features:\n\nGrant access in System Preferences, Security & Privacy.\nRestart your system.";
|
||||
14
mac/Keyman4MacIM/Keyman4MacIM/ckl.lproj/MainMenu.strings
Normal file
14
mac/Keyman4MacIM/Keyman4MacIM/ckl.lproj/MainMenu.strings
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/* Configuration window menu text */
|
||||
"P1b-lE-yFw.title" = "Whalǝr ʻya suyere ani konfigareshon...";
|
||||
|
||||
/* Keyboards menu text */
|
||||
"bQa-j9-nHe.title" = "Kibodyere";
|
||||
|
||||
/* Keyboards menu text */
|
||||
"goP-aK-3WB.title" = "Kibodyere";
|
||||
|
||||
/* About menu text */
|
||||
"kb2-ww-RS3.title" = "Ata kǝra";
|
||||
|
||||
/* On-Screen Keyboard menu text */
|
||||
"s96-JI-YDh.title" = "On Skrin Kibod";
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue