Merge branch 'master' into android-update-catalog-on-startup

This commit is contained in:
safolit 2019-11-19 13:46:14 +07:00 committed by GitHub
commit 20bc90e7b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 461 additions and 149 deletions

12
.editorconfig Normal file
View file

@ -0,0 +1,12 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off

View file

@ -21,6 +21,7 @@ import androidx.appcompat.widget.Toolbar;
import android.content.Context;
import android.content.Intent;
import android.database.DataSetObserver;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
@ -30,6 +31,7 @@ import android.view.Window;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
@ -40,13 +42,16 @@ public final class KeyboardListActivity extends AppCompatActivity implements OnK
private static ListView listView = null;
private static final String TAG = "KeyboardListActivity";
private DataSetObserver repoObserver;
private Dataset repo;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
final Context context = this;
setContentView(R.layout.activity_list_layout);
setContentView(R.layout.activity_list_with_progress_layout);
toolbar = (Toolbar) findViewById(R.id.list_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@ -61,7 +66,20 @@ public final class KeyboardListActivity extends AppCompatActivity implements OnK
String langID = getIntent().getStringExtra("languageCode");
String langName = getIntent().getStringExtra("languageName");
Dataset repo = CloudRepository.shared.fetchDataset(this);
repo = CloudRepository.shared.fetchDataset(this);
// add listener to dataset to get event for catalog update.
repoObserver = new DataSetObserver() {
@Override
public void onChanged() {
updateProgressBar();
}
};
repo.registerDataSetObserver(repoObserver);
// init progress bar state
updateProgressBar();
final FilteredKeyboardsAdapter adapter = new FilteredKeyboardsAdapter(this, repo, langID);
textView.setText(langName);
@ -103,6 +121,25 @@ public final class KeyboardListActivity extends AppCompatActivity implements OnK
});
}
/**
* switch between progress and listview.
*/
private void updateProgressBar()
{
RelativeLayout _progress = findViewById(R.id.progress);
boolean _updaterunning= CloudRepository.shared.updateIsRunning();
ListView _list = findViewById(R.id.listView);
if(_updaterunning)
{
_progress.setVisibility(View.VISIBLE);
_list.setVisibility(View.GONE);
}
else {
_progress.setVisibility(View.GONE);
_list.setVisibility(View.VISIBLE);
}
}
@Override
protected void onResume() {
super.onResume();
@ -117,6 +154,14 @@ public final class KeyboardListActivity extends AppCompatActivity implements OnK
// ensure onKeyboardDownloadFinished() gets called
}
@Override
protected void onDestroy() {
super.onDestroy();
// remove listener from dataset.
repo.unregisterDataSetObserver(repoObserver);
}
@Override
public boolean onSupportNavigateUp() {
onBackPressed();

View file

@ -26,6 +26,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.database.DataSetObserver;
import android.os.Bundle;
import androidx.appcompat.widget.Toolbar;
import android.util.Log;
@ -36,6 +37,7 @@ import android.view.Window;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
@ -56,6 +58,8 @@ public final class LanguageListActivity extends AppCompatActivity implements OnK
// These two JSON objects and their getters are still used by legacy metadata functions.
private static JSONArray languages = null;
private DataSetObserver repoObserver;
private Dataset repo;
protected static JSONArray languages() {
return languages;
@ -78,7 +82,7 @@ public final class LanguageListActivity extends AppCompatActivity implements OnK
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
context = this;
setContentView(R.layout.activity_list_layout);
setContentView(R.layout.activity_list_with_progress_layout);
toolbar = (Toolbar) findViewById(R.id.list_toolbar);
setSupportActionBar(toolbar);
@ -88,11 +92,24 @@ public final class LanguageListActivity extends AppCompatActivity implements OnK
TextView textView = (TextView) findViewById(R.id.bar_title);
textView.setText(getString(R.string.title_add_language));
listView = (ListView) findViewById(R.id.listView);
listView = findViewById(R.id.listView);
listView.setFastScrollEnabled(true);
// Establish the list view based on the CloudRepository's Dataset.
Dataset repo = CloudRepository.shared.fetchDataset(this);
repo = CloudRepository.shared.fetchDataset(this);
// add listener to dataset to get event for catalog update.
repoObserver = new DataSetObserver() {
@Override
public void onChanged() {
updateProgressBar();
}
};
repo.registerDataSetObserver(repoObserver);
// init progress bar state
updateProgressBar();
listView.setAdapter(new LanguagesAdapter(this, repo));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@ -157,6 +174,25 @@ public final class LanguageListActivity extends AppCompatActivity implements OnK
listView.setSelectionFromTop(i.getIntExtra("listPosition", 0), i.getIntExtra("offsetY", 0));
}
/**
* switch between progress and listview.
*/
private void updateProgressBar()
{
RelativeLayout _progress = findViewById(R.id.progress);
boolean _updaterunning= CloudRepository.shared.updateIsRunning();
ListView _list = findViewById(R.id.listView);
if(_updaterunning)
{
_progress.setVisibility(View.VISIBLE);
_list.setVisibility(View.GONE);
}
else {
_progress.setVisibility(View.GONE);
_list.setVisibility(View.VISIBLE);
}
}
@Override
protected void onResume() {
super.onResume();
@ -405,4 +441,11 @@ public final class LanguageListActivity extends AppCompatActivity implements OnK
return !KeyboardPickerActivity.containsKeyboard(context, kbKey);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// remove listener from dataset.
repo.unregisterDataSetObserver(repoObserver);
}
}

View file

@ -135,6 +135,7 @@ public class CloudCatalogDownloadCallback implements ICloudDownloadCallback<Data
// Only empty if no queries returned data - we're offline.
if (jsonTuple.isEmpty()) {
this.failure.run(); // Signal failure to download to our failure callback.
CloudRepository.shared.updateFinished();
return;
}
@ -245,6 +246,9 @@ public class CloudCatalogDownloadCallback implements ICloudDownloadCallback<Data
ensureInitCloudReturn(aContext,aDataSet,aCloudResult);
processCloudReturns(aDataSet, aCloudResult,true);
CloudRepository.shared.updateFinished();
aDataSet.notifyDataSetChanged();
}
@Override

View file

@ -28,6 +28,8 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class CloudRepository {
static public final CloudRepository shared = new CloudRepository();
@ -42,6 +44,9 @@ public class CloudRepository {
// DEBUG: Never allow these to be `true` in production.
private static final boolean DEBUG_DISABLE_CACHE = false;
private boolean updateIsRunning = false;
private CloudRepository() {
// Tracks the time of the most recent cache. We start at null to indicate that we haven't
@ -123,9 +128,18 @@ public class CloudRepository {
{
String deviceType = CloudDataJsonUtil.getDeviceTypeForCloudQuery(aContext);
// Sanitize appVersion to #.#.# to match the API spec
// Regex needs to match the entire string
String appVersion = BuildConfig.VERSION_NAME;
Pattern pattern = Pattern.compile("^(\\d+\\.\\d+\\.\\d+).*");
Matcher matcher = pattern.matcher(appVersion);
if (matcher.matches() && matcher.groupCount() >= 1) {
appVersion = matcher.group(1);
}
// Retrieves the cloud-based keyboard catalog in Android's preferred format.
String keyboardURL = String.format("%s?version=%s&device=%s&languageidtype=bcp47",
KMKeyboardDownloaderActivity.kKeymanApiBaseURL, BuildConfig.VERSION_NAME, deviceType);
KMKeyboardDownloaderActivity.kKeymanApiBaseURL, appVersion, deviceType);
//cloudQueries[cloudQueryEntries++] = new CloudApiParam(ApiTarget.Keyboards, keyboardURL, JSONType.Object);
return new CloudApiTypes.CloudApiParam(
@ -361,6 +375,7 @@ public class CloudRepository {
String msg = context.getString(R.string.catalog_download_is_running_in_background);
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
} else {
updateIsRunning = true;
String msg = context.getString(R.string.catalog_download_start_in_background);
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
CloudDownloadMgr.getInstance().executeAsDownload(
@ -382,11 +397,12 @@ public class CloudRepository {
}
public void updateFinished()
{
updateIsRunning=false;
}
public boolean updateIsRunning() {
return updateIsRunning;
}
}

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<include layout="@layout/list_toolbar" />
<include layout="@layout/progress_layout" />
<include layout="@layout/list_layout" />
</LinearLayout>

View file

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/fab_margin"
>
<ProgressBar
android:id="@+id/progressbar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"/>
<TextView
android:id="@+id/progressbarmessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/progress_message_catalog_download_is_running"
android:layout_centerInParent="true"
android:layout_below="@+id/progressbar" />
</RelativeLayout>

View file

@ -66,6 +66,7 @@
<!-- Background download messages-->
<string name="catalog_download_start_in_background" translatable="false">Catalog update started in background.\n</string>
<string name="catalog_download_is_running_in_background" translatable="false">The catalog is still downloading; please try again in a moment!\n</string>
<string name="progress_message_catalog_download_is_running" translatable="false">The catalog is still downloading!\n</string>
<string name="keyboard_download_start_in_background" translatable="false">Downloading keyboard started in Background</string>
<string name="keyboard_download_is_running_in_background" translatable="false">The selected keyboard is already downloading; please try again in a moment!</string>
<string name="keyboard_download_finished" translatable="false">Keyboard download is finished!</string>

View file

@ -4,14 +4,15 @@
* Start version 13.0
* New Features:
* Adding a download manager to execute downloads in background and cleanup the existing implementation (#2247, #2275, #2308)
* Show spinner (without blocking UI), if user wants to add a language/keyboard and catalog download is in progress (#2313)
* Improve custom package installation: Show readme.htm before starting installation process (#2286)
* Update target Android SDK version to 29 (#2279)
* Add linting to Debug builds and resolve lint errors (#2305)
* Sanitize the app version to `#.#.#` for the API cloud query (#2319)
* Check for keyboard updates during keyman startup (#2335)
* Show available keyboard updates as android system notifications (#2335)
* Add update indicator icon to inform user about updates and install updates in keyman app (#2335)
## 2019-10-30 12.0.4206 stable
* Bug fix:
* Disable suggestions when system keyboard entering password field (#2255)

5
linux/Jenkinsfile vendored
View file

@ -4,4 +4,7 @@
@Library('lsdev-pipeline-library') _
keymanPackaging
keymanPackaging {
distributionsToPackage = 'xenial bionic'
arches = 'amd64 i386'
}

View file

@ -20,10 +20,10 @@
# any=@precise.any
[common]
any=git autotools-dev build-essential dh-autoreconf libibus-1.0-dev flex bison
any=git autotools-dev build-essential dh-autoreconf libibus-1.0-dev flex bison meson
[xenial]
any=libx11-dev
[bionix]
[bionic]
any=@xenial.any

View file

@ -0,0 +1,4 @@
# Editor configuration, see https://editorconfig.org
[*]
indent_style = space
indent_size = 4

View file

@ -15,32 +15,37 @@ extra_projects="keyboardprocessor keyman-config"
if [ "$1" != "" ]; then
if [ "$1" == "keyboardprocessor" ]; then
echo "reconfiguring only keyboardprocessor"
echo "reconfiguring only keyboardprocessor"
extra_projects="keyboardprocessor"
autotool_projects=""
elif [ ! -d "$1" ]; then
echo "project $1 does not exist"
exit 1
elif [ "$1" == "keyman-config" ]; then
echo "reconfiguring only keyman-config"
echo "reconfiguring only keyman-config"
extra_projects="keyman-config"
autotool_projects=""
else
echo "reconfiguring only $1"
echo "reconfiguring only $1"
autotool_projects="$1"
extra_projects=""
fi
fi
JENKINS=${JENKINS:="no"}
oldvers=`cat VERSION`
if [ -n "$SKIPVERSION" -a -f OLDVERSION ]; then
oldvers=$(cat OLDVERSION)
newvers=$(cat VERSION)
else
JENKINS=${JENKINS:="no"}
oldvers=`cat VERSION`
. $(dirname "$0")/version.sh
. $(dirname "$0")/version.sh
version
version
echo "version: ${newvers}"
echo "${newvers}" > VERSION
echo "version: ${newvers}"
echo "${newvers}" > VERSION
fi
# autoreconf the projects
for proj in ${autotool_projects}; do

View file

@ -2,10 +2,17 @@
## 13.0 alpha
* Start version 13.0
* Testing for upcoming patch to stable:
* Fixes issue with mnemonic keyboard handling of backspace and delete keys (#2288)
* Fix for iOS Safari's "Request Desktop Website" option disabling touch interactivity (#2283)
* Fix for keyboards using rules with the `nul` statement that replace the full context (#2284)
## 2019-11-13 12.0.102 stable
* Fixes issue with mnemonic keyboard handling of backspace and delete keys (#2288)
* Fix for iOS Safari's "Request Desktop Website" option disabling touch interactivity (#2283)
* Fix for keyboards using rules with the `nul` statement that replace the full context (#2284)
## 2019-10-10 12.0.101 stable
* Fixes issue with keyboards requiring special state notifications, such as the CJK picker keyboards (#2194)
## 2019-10-07 12.0.100 stable
* Release 12.0
## 2019-10-04 12.0.90 beta
* Fixes next-layer management complications with predictive correction data computation (#2172)

6
web/package-lock.json generated
View file

@ -2314,9 +2314,9 @@
}
},
"typescript": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.2.tgz",
"integrity": "sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==",
"version": "3.7.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.2.tgz",
"integrity": "sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==",
"dev": true
},
"uc.micro": {

View file

@ -37,7 +37,7 @@
"karma-teamcity-reporter": "^1.1.0",
"mocha": "^5.2.0",
"modernizr": "^3.7.1",
"typescript": "^3.2.2"
"typescript": "^3.7.2"
},
"scripts": {
"tsc": "tsc",

View file

@ -1,3 +1,6 @@
// Includes version-related functionality
///<reference path="utils/version.ts"/>
// The Device object definition -------------------------------------------------
namespace com.keyman {

View file

@ -23,8 +23,6 @@ interface Element {
kmwInput: boolean,
_kmwResizeHandler: (e: any) => void,
onselectstart: any,
// Used by our util.wait / util.alert system
dismiss: () => void
}

View file

@ -204,7 +204,7 @@ namespace com.keyman.osk {
ts.fontFamily=spec['font'];
}
if(typeof spec['fontsize'] == 'string' && spec['fontsize'] != 0) {
if(typeof spec['fontsize'] == 'string' && spec['fontsize'] != '') {
ts.fontSize=spec['fontsize'];
}
@ -564,12 +564,12 @@ namespace com.keyman.osk {
// Function fields (fleshed out by kmwnative.ts and/or kmwembedded.ts)
touchHold: (key: KeyElement) => void;
optionKey: (e: KeyElement, keyName: string, keyDown: boolean) => void;
highlightSubKeys: (key: KeyElement, x: number, y: number) => void = this.highlightSubKeys || function(k,x,y) {};
highlightSubKeys: (key: KeyElement, x: number, y: number) => void;
showKeyTip: (key: KeyElement, on: boolean) => void;
drawPreview: (canvas: HTMLCanvasElement, w: number, h: number, edge: number) => void = this.drawPreview || function(c,w,h,e) {};
drawPreview: (canvas: HTMLCanvasElement, w: number, h: number, edge: number) => void;
createKeyTip: () => void;
addCallout: (key: KeyElement) => HTMLDivElement = this.addCallout || function(key) {return null};
waitForFonts: (kfd,ofd) => boolean = this.waitForFonts || function(kfd,ofd){return true;}; // Default is used by embedded.
addCallout: (key: KeyElement) => HTMLDivElement;
waitForFonts: (kfd,ofd) => boolean;
//#region OSK constructor and helpers
@ -581,6 +581,14 @@ namespace com.keyman.osk {
* Description Generates the base visual keyboard element, prepping for attachment to KMW
*/
constructor(PVK, Lhelp, layout0: LayoutFormFactor, kbdBitmask: number) {
// Add handler stubs if not otherwise defined. (We can no longer in-line default-define with the declaration.)
this.highlightSubKeys = this.highlightSubKeys || function(k,x,y) {};
this.drawPreview = this.drawPreview || function(c,w,h,e) {};
this.addCallout = this.addCallout || function(key) {return null};
this.waitForFonts = this.waitForFonts || function(kfd,ofd){return true;}; // Default is used by embedded.
// Do normal constructor stuff.
let keyman = com.keyman.singleton;
let util = keyman.util;
@ -617,7 +625,12 @@ namespace com.keyman.osk {
}
// Set flag to add default (US English) key label if specified by keyboard
layout.keyLabels = activeKeyboard && ((typeof(activeKeyboard['KDU']) != 'undefined') && activeKeyboard['KDU']);
if(typeof layout['displayUnderlying'] != 'undefined') {
layout.keyLabels = layout['displayUnderlying'] == true; // force bool
} else {
layout.keyLabels = activeKeyboard && ((typeof(activeKeyboard['KDU']) != 'undefined') && activeKeyboard['KDU']);
}
let divLayerContainer = this.deviceDependentLayout(layout, util.device.formFactor);
this.ddOSK = true;
@ -2126,7 +2139,11 @@ namespace com.keyman.osk {
// Cannot create an OSK if no layout defined, just return empty DIV
if(layout != null) {
layout.keyLabels=((typeof(PKbd['KDU']) != 'undefined') && PKbd['KDU']);
if(typeof layout['displayUnderlying'] != 'undefined') {
layout.keyLabels = layout['displayUnderlying'] == true; // force bool
} else {
layout.keyLabels = typeof(PKbd['KDU']) != 'undefined' && PKbd['KDU'];
}
}
// TODO: Fix this method's link!

View file

@ -1,3 +1,6 @@
// Ensure that this class contains no reference into core KMW code - it is referenced
// by components intended to be modular and possible to separate from core KMW.
namespace com.keyman.utils {
// Dotted-decimal version
export class Version {

View file

@ -3,6 +3,19 @@
## 13.0 alpha
* Start version 13.0
## 2019-11-15 12.0.54 stable
* Bug Fix: On Screen Keyboard restored to wrong screen and position when reloading (#2330)
## 2019-11-12 12.0.53 stable
* Bug Fix: Address instability when exiting Keyman on some systems (#2324)
* Bug Fix: Keyman was not working with Skype, Windows Search on some systems (#2324)
## 2019-10-20 12.0.52 stable
* Upgrades from 11.0 and 12.0 were losing installed keyboards and settings (#2214)
## 2019-10-07 12.0.50 stable
* Release 12.0
## 2019-10-03 12.0.42 beta
* Bug Fix: Additional shutdown issues that sporadically occurred (#2157)
* Fix issues starting debug logging for non-administrative users (#2153)

View file

@ -327,9 +327,11 @@ begin
SetLength(FPackageJSFileInfos, Length(FPackageJSFileInfos)+1);
FPackageJSFileInfos[High(FPackageJSFileInfos)].Filename := Zip.FileNames[j];
// For now, apply JS keyboard to all web and mobile targets
// Apply JS keyboard only to mobile targets, because web is not supported
// in a package. If a package does not support mobile, it should not include
// the .js.
// Not using GetKeyboardInfo because that only handles kmx files
FPackageJSFileInfos[High(FPackageJSFileInfos)].Info.Targets := 'web mobile';
FPackageJSFileInfos[High(FPackageJSFileInfos)].Info.Targets := 'mobile';
end;
end;
end;
@ -976,8 +978,6 @@ begin
AddNewPair('windows', 'full');
AddNewPair('macos', 'full');
AddNewPair('linux', 'full');
AddNewPair('desktopWeb', 'full');
AddNewPair('mobileWeb', 'full');
AddNewPair('android', 'full');
AddNewPair('ios', 'full');
end
@ -1008,11 +1008,6 @@ begin
// FPackageKMXFileInfos can contain target information for web/mobile targets.
// This is a current limitation of FPackageJSFileInfos if there's no kmx files
if target = ktWeb then
begin
AddNewPair('desktopWeb', 'full');
AddNewPair('mobileWeb', 'full');
end;
if (target = ktMobile) then
begin
AddNewPair('android', 'full');
@ -1035,11 +1030,6 @@ begin
targets := StringToKeymanTargets(keyboardFile.Info.Targets);
for target in targets do
begin
if (target = ktWeb) then
begin
AddNewPair('desktopWeb', 'full');
AddNewPair('mobileWeb', 'full');
end;
if (target = ktMobile) then
begin
AddNewPair('android', 'full');
@ -1056,16 +1046,17 @@ begin
end;
end;
// Handle JS file not in kmp
if FJsFile <> '' then
// Handle JS file not in kmp. Because it is isolated, we cannot detect
// whether it supports mobile vs desktop web because that is not included
// in the .js. So, for now we assume both.
//
// We no longer assume that the presence of a .js means support for
// native mobile apps. These apps now work on the basis of having a
// .kmp file available
if (FJsFile <> '') then
begin
AddNewPair('desktopWeb', 'full');
AddNewPair('mobileWeb', 'full');
// TODO: Don't add Android and iOS when we complete the addition of all .js keyboards
// to packages in the repository (including legacy keyboards)
AddNewPair('android', 'full');
AddNewPair('ios', 'full');
end;
json.AddPair('platformSupport', v);

View file

@ -34,7 +34,6 @@ inherited frameCEFHost: TframeCEFHost
Top = 208
end
object cef: TChromium
OnWidgetCompMsg = cefWidgetCompMsg
OnLoadEnd = cefLoadEnd
OnSetFocus = cefSetFocus
OnRunContextMenu = cefRunContextMenu

View file

@ -35,7 +35,6 @@ const
CEF_AFTERCREATE = WM_USER + 302;
CEF_SHOW = WM_USER + 303;
CEF_LOADEND = WM_USER + 304;
CEF_SETFOCUS = WM_USER + 305;
CEF_KEYEVENT = WM_USER + 306;
CEF_BEFOREBROWSE = WM_USER + 307;
CEF_CONSOLEMESSAGE = WM_USER + 308;
@ -106,7 +105,6 @@ type
var settings: TCefBrowserSettings;
var noJavascriptAccess: Boolean;
var Result: Boolean);
procedure cefWidgetCompMsg(var aMessage: TMessage; var aHandled: Boolean);
procedure cefSetFocus(Sender: TObject; const browser: ICefBrowser;
source: TCefFocusSource; out Result: Boolean);
private
@ -134,7 +132,6 @@ type
procedure Handle_CEF_AFTERCREATE(var Message: TMessage);
procedure Handle_CEF_SHOW(var message: TMessage);
procedure Handle_CEF_LOADEND(var message: TMessage);
procedure Handle_CEF_SETFOCUS(var message: TMessage);
procedure Handle_CEF_KEYEVENT(var message: TMessage);
procedure Handle_CEF_BEFOREBROWSE(var message: TMessage);
procedure Handle_CEF_CONSOLEMESSAGE(var message: TMessage);
@ -268,14 +265,6 @@ begin
CreateBrowser;
end;
procedure TframeCEFHost.cefWidgetCompMsg(var aMessage: TMessage;
var aHandled: Boolean);
begin
AssertCefThread;
if aMessage.Msg = WM_SETFOCUS then
PostMessage(FCallbackWnd, CEF_SETFOCUS, 0, 0);
end;
procedure TframeCEFHost.CreateBrowser;
begin
AssertVclThread;
@ -308,9 +297,11 @@ end;
procedure TframeCEFHost.SetFocus;
begin
AssertVclThread;
inherited;
if not FIsClosing and cefwp.CanFocus then
cefwp.SetFocus;
if not FIsClosing and cefwp.CanFocus and Assigned(cef) then
begin
GetParentForm(Self).ActiveControl := Self;
cef.SetFocus(True);
end;
end;
procedure TframeCEFHost.CallbackWndProc(var Message: TMessage);
@ -322,7 +313,6 @@ begin
CEF_AFTERCREATE: Handle_CEF_AFTERCREATE(Message);
CEF_SHOW: Handle_CEF_SHOW(Message);
CEF_LOADEND: Handle_CEF_LOADEND(Message);
CEF_SETFOCUS: Handle_CEF_SETFOCUS(Message);
CEF_KEYEVENT: Handle_CEF_KEYEVENT(Message);
CEF_BEFOREBROWSE: Handle_CEF_BEFOREBROWSE(Message);
CEF_CONSOLEMESSAGE: Handle_CEF_CONSOLEMESSAGE(Message);
@ -534,13 +524,6 @@ begin
FOnLoadEnd(Self);
end;
procedure TframeCEFHost.Handle_CEF_SETFOCUS(var message: TMessage);
begin
AssertVclThread;
if Assigned(cefwp) and cefwp.Visible and cefwp.CanFocus then
GetParentForm(cefwp).ActiveControl := cefwp;
end;
procedure TframeCEFHost.cefPreKeyEvent(Sender: TObject;
const browser: ICefBrowser; const event: PCefKeyEvent; osEvent: PMsg;
out isKeyboardShortcut, Result: Boolean);
@ -614,7 +597,7 @@ end;
procedure TframeCEFHost.cefSetFocus(Sender: TObject; const browser: ICefBrowser;
source: TCefFocusSource; out Result: Boolean);
begin
Result := source <> FOCUS_SOURCE_NAVIGATION;
Result := source = FOCUS_SOURCE_NAVIGATION;
end;
procedure TframeCEFHost.WMEnterMenuLoop(var aMessage: TMessage);

View file

@ -656,7 +656,6 @@ end;
procedure TframeTextEditor.SetFocus;
begin
inherited;
cef.SetFocus;
end;

View file

@ -3,6 +3,12 @@
## 13.0 alpha
* Start version 13.0
## 2019-11-15 12.0.54 stable
* Bug Fix: Text editor and other controls did not receive focus correctly (#2331)
## 2019-10-07 12.0.50 stable
* Release 12.0
## 2019-10-05 12.0.45 beta
* Touch Layout Editor: Make default padding in touch layout editor match default padding in KeymanWeb. (#2170)

View file

@ -442,6 +442,10 @@ begin
begin
olestrm := TOLEStream.Create(istrm);
try
// In some situations, launching the app multiple times rapidly can
// cause the icon to be loaded multiple times. Make sure we reset the
// stream position before we try and read.
olestrm.Position := 0;
Application.Icon.LoadFromStream(olestrm);
finally
olestrm.Free;

View file

@ -1,7 +1,5 @@
program keyman;
uses
Forms,
Dialogs,
@ -148,6 +146,11 @@ uses
{$R VERSION.RES}
{$R MANIFEST.RES}
//
// PEOPTFLAGS $140 turns on Data Execution Prevention
//
{$SETPEOPTFLAGS $140}
begin
//InitTntEnvironment;
//ShowMessage('Start');

View file

@ -280,6 +280,8 @@ type
implementation
uses
System.Types,
CommCtrl,
GraphUtil,
KeymanHints,
@ -303,7 +305,7 @@ uses
VistaMessages,
MessageIdentifierConsts,
messageidentifiers,
BitmapIPicture, Types;
BitmapIPicture;
{$R *.DFM}
@ -1331,12 +1333,62 @@ begin
end;
procedure TfrmVisualKeyboard.LoadSettings;
procedure MoveBounds(R: TRect);
function FitRectInBounds(R, BR: TRect): TRect;
begin
if (R.Left < R.Right) and (R.Top < R.Bottom) and (R.Left >= 0) and (R.Top >= 0) and
(R.Bottom <= Screen.Height) and (R.Right <= Screen.Width) then
BoundsRect := R;
if R.Width > BR.Width then
R.Width := BR.Width;
if R.Height > BR.Height then
R.Height := BR.Height;
if R.Left < BR.Left then
R.Offset(BR.Left-R.Left, 0);
if R.Right > BR.Right then
R.Offset(BR.Right-R.Right, 0);
if R.Top < BR.Top then
R.Offset(0, BR.Top-R.Top);
if R.Bottom > BR.Bottom then
R.Offset(0, BR.Bottom-R.Bottom);
Result := R;
end;
procedure MoveBounds(R: TRect);
var
area, i: Integer;
m: Integer;
BR, RI: TRect;
begin
// Adjust the rectangle to ensure TopLeft <= BottomRight
R.NormalizeRect;
// Move the rect onto the screen (e.g. when monitor is disconnected,
// we don't want to show the OSK off the screen). It is valid for the window
// rect to go negative, if for example primary monitor is not left-most.
// If the OSK is partially on-screen, move it onto the monitor where it has
// the most real-estate.
m := 0; area := 0;
for i := 0 to Screen.MonitorCount - 1 do
begin
if System.Types.IntersectRect(RI, R, Screen.Monitors[i].BoundsRect) then
begin
if RI.Width * RI.Height > area then
begin
area := RI.Width * RI.Height;
m := i;
end;
end;
end;
BR := Screen.Monitors[m].WorkareaRect;
Self.BoundsRect := FitRectInBounds(R, BR);
end;
procedure MoveDefault;
begin
SetBounds(Screen.WorkAreaRect.Right - Width, Screen.WorkAreaRect.Bottom - Height, Width, Height);

View file

@ -157,11 +157,13 @@ BOOL __stdcall DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID reserved)
case DLL_PROCESS_ATTACH:
//if(!TestDebugProcess()) return FALSE;
//if(!ShouldAttachToProcess()) return FALSE;
OutputThreadDebugString("DLL_PROCESS_ATTACH");
if(!Globals_InitProcess()) return FALSE;
break;
case DLL_PROCESS_DETACH:
//if(!TestDebugProcess()) return FALSE;
if (reserved == NULL) {
OutputThreadDebugString("DLL_PROCESS_DETACH not terminating");
// If reserved == NULL, that means the library is being unloaded, but
// the process is not terminating.
//
@ -180,15 +182,20 @@ BOOL __stdcall DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID reserved)
// CloseTSF from here. This needs further investigation...
UninitialiseProcess(FALSE);
Globals_UninitProcess();
}
else {
OutputThreadDebugString("DLL_PROCESS_DETACH terminating");
}
break;
case DLL_THREAD_ATTACH:
//if(!TestDebugProcess()) return FALSE;
OutputThreadDebugString("DLL_THREAD_ATTACH");
Globals_InitThread();
break;
case DLL_THREAD_DETACH:
//if(!TestDebugProcess()) return FALSE;
UninitialiseProcess(FALSE);
OutputThreadDebugString("DLL_THREAD_DETACH");
UninitialiseProcess(FALSE);
Globals_UninitThread();
break;
}
@ -214,8 +221,13 @@ BOOL UninitialiseProcess(BOOL Lock)
if(_td->IndexStack) delete _td->IndexStack;
_td->IndexStack = NULL;
}
if (_td->miniContext) delete _td->miniContext;
_td->miniContext = NULL;
if (_td->msgbuf) delete _td->msgbuf;
_td->msgbuf = NULL;
}
return TRUE;
}
@ -512,6 +524,10 @@ extern "C" BOOL _declspec(dllexport) WINAPI Keyman_Exit(void)
return FALSE;
}
#ifndef _WIN64
Hotkeys::Unload();
#endif
*Globals::InitialisingThread() = 0;
BOOL RetVal = TRUE;
@ -801,6 +817,7 @@ void LoadBaseLayoutSettings() { // I4552 // I4583
void RefreshKeyboards(BOOL Initialising)
{
OutputThreadDebugString("RefreshKeyboards");
char sz[_MAX_FNAME];
char oldname[_MAX_FNAME];
RegistryReadOnly *reg2;
@ -828,7 +845,7 @@ void RefreshKeyboards(BOOL Initialising)
_td->ActiveKeymanID = KEYMANID_NONKEYMAN;
}
ReleaseKeyboards(TRUE);
ReleaseKeyboards(TRUE);
/* Read the "keyboard off hotkey", simulate Alt+Gr, Hotkeys-Toggle flags */
@ -938,13 +955,14 @@ void RefreshKeyboards(BOOL Initialising)
_td->FInRefreshKeyboards = FALSE;
}
void ReleaseKeyboards(BOOL Lock)
{
OutputThreadDebugString("ReleaseKeyboards");
PKEYMAN64THREADDATA _td = ThreadGlobals();
if(!_td || _td->lpKeyboards) return;
if(!_td || !_td->lpKeyboards) return;
if(Lock) if(_td->lpActiveKeyboard && !_td->ForceFileName[0]) DeactivateDLLs(_td->lpActiveKeyboard);
if(Lock) if(_td->lpActiveKeyboard && !_td->ForceFileName[0]) DeactivateDLLs(_td->lpActiveKeyboard);
for(int i = 0; i < _td->nKeyboards; i++)
{

View file

@ -15,45 +15,48 @@ typedef BOOL
IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
);
DWORD ExceptionMessage(LPSTR Proc, LPEXCEPTION_POINTERS ep) {
#ifndef _DEBUG
UNREFERENCED_PARAMETER(Proc);
#endif
DWORD ExceptionMessage(LPSTR Proc, LPEXCEPTION_POINTERS ep)
{
MINIDUMP_EXCEPTION_INFORMATION mei;
char filename[MAX_PATH], temppath[MAX_PATH];
if(GetTempPath(MAX_PATH, temppath) == 0 ||
GetTempFileName(temppath, "kmc", 0, filename) == 0)
SendDebugMessageFormat(0, sdmGlobal, 0, "Minidump failed to generate temp file name");
else
{
if (GetTempPath(MAX_PATH, temppath) == 0 ||
GetTempFileName(temppath, "kmc", 0, filename) == 0) {
OutputThreadDebugString("Minidump failed to generate temp file name\n");
}
else {
HANDLE hFile = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if(!hFile)
SendDebugMessageFormat(0, sdmGlobal, 0, "Minidump failed to create file %s", filename);
else
{
if (!hFile) {
OutputThreadDebugString("Minidump failed to create file ");
OutputThreadDebugString(filename);
}
else {
mei.ClientPointers = TRUE;
mei.ExceptionPointers = ep;
mei.ThreadId = GetCurrentThreadId();
HMODULE hDbgHelp = LoadLibrary("dbghelp.dll");
if(!hDbgHelp)
SendDebugMessage(0, sdmGlobal, 0, "dbghelp.dll not available");
else
{
if (!hDbgHelp) {
OutputThreadDebugString("dbghelp.dll not available");
}
else {
PMiniDumpWriteDump mdwd = (PMiniDumpWriteDump) GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
if(!mdwd)
SendDebugMessage(0, sdmGlobal, 0, "MiniDumpWriteDump not available");
else
{
if (!mdwd) {
OutputThreadDebugString("MiniDumpWriteDump not available");
}
else {
if (!(*mdwd)(GetCurrentProcess(), GetCurrentProcessId(), hFile,
(MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithHandleData),
&mei, NULL, NULL))
(MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithHandleData),
&mei, NULL, NULL)) {
DebugLastError("MiniDumpWriteDump");
else
{
SendDebugMessageFormat(0, sdmGlobal, 0, "Minidump written to %s", filename);
}
else {
OutputThreadDebugString("Minidump written to ");
OutputThreadDebugString(filename);
HKEY hkey;
if(RegCreateKeyEx(HKEY_CURRENT_USER, REGSZ_KeymanEngineDiag, 0, NULL, 0, KEY_ALL_ACCESS,
NULL, &hkey, NULL) == ERROR_SUCCESS)
{
NULL, &hkey, NULL) == ERROR_SUCCESS) {
DWORD v = 0;
RegSetValueEx(hkey, filename, 0, REG_DWORD, (PBYTE)&v, sizeof(DWORD));
RegCloseKey(hkey);
@ -66,23 +69,26 @@ DWORD ExceptionMessage(LPSTR Proc, LPEXCEPTION_POINTERS ep)
}
}
if(!ep || !ep->ExceptionRecord)
{
SendDebugMessageFormat(0, sdmGlobal, 0, "CAUGHT UNKNOWN EXCEPTION");
if(!ep || !ep->ExceptionRecord) {
OutputThreadDebugString("CAUGHT UNKNOWN EXCEPTION");
return EXCEPTION_CONTINUE_SEARCH;
}
LPEXCEPTION_RECORD er = ep->ExceptionRecord;
while(er != NULL)
{
if(er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
SendDebugMessageFormat(0, sdmGlobal, 0, "CAUGHT EXCEPTION %d (ACCESS VIOLATION) IN %s AT %x; attempted to %s %x",
er->ExceptionCode, Proc, er->ExceptionAddress,
er->ExceptionInformation[0] == 0 ? "read from" : "write to",
er->ExceptionInformation[1]);
else
SendDebugMessageFormat(0, sdmGlobal, 0, "CAUGHT EXCEPTION %d IN %s AT %x",
er->ExceptionCode, Proc, er->ExceptionAddress);
while(er != NULL) {
if (er->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
OutputThreadDebugString("EXCEPTION_ACCESS_VIOLATION in ");
OutputThreadDebugString(Proc);
/*SendDebugMessageFormat(0, sdmGlobal, 0, "CAUGHT EXCEPTION %d (ACCESS VIOLATION) IN %s AT %x; attempted to %s %x",
er->ExceptionCode, Proc, er->ExceptionAddress,
er->ExceptionInformation[0] == 0 ? "read from" : "write to",
er->ExceptionInformation[1]);*/
}
else {
OutputThreadDebugString("CAUGHT EXCEPTION");
/*SendDebugMessageFormat(0, sdmGlobal, 0, "CAUGHT EXCEPTION %d IN %s AT %x",
er->ExceptionCode, Proc, er->ExceptionAddress);*/
}
er = er->ExceptionRecord;
}

View file

@ -45,6 +45,18 @@ Hotkeys *Hotkeys::Instance() { // I4326
return g_Hotkeys;
}
void Hotkeys::Unload() {
if (GetCurrentThreadId() != Globals::get_InitialisingThread()) {
OutputThreadDebugString("Unexpected: no other thread should be attempting to unload hotkeys");
return;
}
if (g_Hotkeys != NULL) {
delete g_Hotkeys;
g_Hotkeys = NULL;
}
}
void Hotkeys::Reload() { // I4326 // I4390
Hotkeys *hotkeys = Hotkeys::Instance(); // I4641
if(hotkeys == NULL) {

View file

@ -43,4 +43,5 @@ public:
Hotkey *GetHotkey(DWORD hotkey);
static void Reload(); // I4326
static Hotkeys *Instance(); // I4326
};
static void Unload();
};

View file

@ -304,4 +304,12 @@ char *Debug_UnicodeString(PWSTR s, int x) {
}
//WideCharToMultiByte(CP_ACP, 0, buf, -1, bufout, 128, NULL, NULL);
return bufout[x];
}
}
#ifdef _DEBUG
void _OutputThreadDebugString(char *s) {
char buf[256];
sprintf_s(buf, "[%d]: %s\n", GetCurrentThreadId(), s);
OutputDebugString(buf);
}
#endif

View file

@ -138,7 +138,11 @@ PKEYMAN64THREADDATA Globals_InitThread()
void Globals_UninitThread()
{
if(!Globals_ProcessInitialised()) return;
OutputThreadDebugString("Globals_UninitThread");
if (!Globals_ProcessInitialised()) {
OutputThreadDebugString("Globals_UninitThread aborted without cleanup");
return;
}
CloseTSF(); // I3933
@ -185,6 +189,7 @@ void Globals_UninitProcess()
TlsFree(dwTlsIndex);
dwTlsIndex = TLS_OUT_OF_INDEXES;
LeaveCriticalSection(&csGlobals);
DeleteCriticalSection(&csGlobals);
}
PKEYMAN64THREADDATA ThreadGlobals()

View file

@ -53,7 +53,7 @@ LRESULT CALLBACK kmnLowLevelKeyboardProc(
__except(ExceptionMessage("kmnLowLevelKeyboardProc", GetExceptionInformation())) {
}
#endif
return res;
return res;
}
BOOL KeyLanguageSwitchPress(WPARAM wParam, BOOL extended, BOOL isUp, DWORD ShiftState);

View file

@ -41,9 +41,10 @@ void LoadSharedKeyboardOptions(LPINTKEYBOARDINFO kp)
void FreeKeyboardOptions(LPINTKEYBOARDINFO kp)
{
assert(kp != NULL);
assert(kp->Keyboard != NULL);
assert(kp->KeyboardOptions != NULL);
// This is a cleanup routine; we don't want to precondition all calls to it
// so we do not assert
if (kp == NULL || kp->Keyboard == NULL || kp->KeyboardOptions == NULL)
return;
for(DWORD i = 0; i < kp->Keyboard->cxStoreArray; i++)
if(kp->KeyboardOptions[i].Value)

View file

@ -66,6 +66,7 @@ LRESULT _kmnCallWndProc(int nCode, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK kmnCallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
LRESULT res = 0;
#ifdef _DEBUG_EXCEPTION
res = _kmnCallWndProc(nCode,wParam,lParam);
#else
@ -77,7 +78,7 @@ LRESULT CALLBACK kmnCallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
}
#endif
return res;
return res;
}
// I3617
BOOL IsSysTrayWindow(HWND hwnd);

View file

@ -149,6 +149,7 @@ public:
};
void ISerialKeyEventClient::Startup() {
OutputThreadDebugString("ISerialKeyEventClient::Startup");
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (_td) {
_td->pSerialKeyEventClient = new SerialKeyEventClient();
@ -156,6 +157,7 @@ void ISerialKeyEventClient::Startup() {
}
void ISerialKeyEventClient::Shutdown() {
OutputThreadDebugString("ISerialKeyEventClient::Shutdown");
PKEYMAN64THREADDATA _td = ThreadGlobals();
if (_td && _td->pSerialKeyEventClient) {
delete _td->pSerialKeyEventClient;

View file

@ -133,7 +133,7 @@ begin
end;
writeln(FLogFile, Format('%12.12d ', [GetTickCount()])+FormatDateTime('yyyy-mm-dd hh:nn:ss.zzz', Now) + ': ' + StringOfChar(' ', FMethodStack.Count*2) + msg);
flush(FLogFile);
OutputDebugString(PChar(msg));
OutputDebugString(PChar('KLog:' + msg + #13#10));
{$ENDIF}
end;

View file

@ -347,6 +347,13 @@ BOOL ShouldDebug_1(); // TSDMState state);
#endif
#ifdef _DEBUG
#define OutputThreadDebugString(s) _OutputThreadDebugString(s)
void _OutputThreadDebugString(char *s);
#else
#define OutputThreadDebugString(s)
#endif
/* Keyboard selection functions */
void HandleRefresh(int code, LONG tag);