Skip to content

Commit

Permalink
Windows Keyboarding Refactor
Browse files Browse the repository at this point in the history
* Support Windows system keyboards in 64bit
* Drop XP Support
* Unify Keyman10 and Windows keyboard behavior
  • Loading branch information
jasonleenaylor committed Apr 13, 2018
1 parent 183fcfd commit 7b774e9
Show file tree
Hide file tree
Showing 16 changed files with 142 additions and 688 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,6 @@
<Compile Include="LinuxKeyboardControllerTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WindowsKeyboardControllerTests.cs" />
<Compile Include="WinKeyboardAdaptorTests.cs" />
<Compile Include="XkbKeyboardAdapterTests.cs" />
<Compile Include="XklEngineTests.cs" />
</ItemGroup>
Expand Down
43 changes: 0 additions & 43 deletions SIL.Windows.Forms.Keyboarding.Tests/WinKeyboardAdaptorTests.cs

This file was deleted.

4 changes: 2 additions & 2 deletions SIL.Windows.Forms.Keyboarding/KeyboardController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -523,9 +523,9 @@ public IKeyboardDefinition ActiveKeyboard

internal static void GetLayoutAndLocaleFromLanguageId(string id, out string layout, out string locale)
{
var parts = id.Split('_');
var parts = id.Split('_', '-');
locale = parts[0];
layout = parts.Length > 1 ? parts[1] : String.Empty;
layout = parts.Length > 1 ? parts[parts.Length - 1] : String.Empty;
}
}
}
5 changes: 5 additions & 0 deletions SIL.Windows.Forms.Keyboarding/KeyboardDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ protected virtual bool DeactivatePreviousKeyboard(IKeyboardDefinition keyboardTo
/// </summary>
public override void Activate()
{
if (Keyboard.Controller.ActiveKeyboard == this)
{
// Don't waste the time, energy, and buggy behavior with IMEs to 'reactivate' ourself.
return;
}
var activeKeyboard = Keyboard.Controller.ActiveKeyboard as KeyboardDescription;
if (activeKeyboard != null && activeKeyboard.DeactivatePreviousKeyboard(this))
activeKeyboard.Deactivate();
Expand Down
9 changes: 4 additions & 5 deletions SIL.Windows.Forms.Keyboarding/Linux/IIbusEventHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ namespace SIL.Windows.Forms.Keyboarding.Linux
/// allow the keyboard to manage the pre-edit text.
///
/// If the application implements this interface it will need to pass an instance
/// when registering the control (<see cref="KeyboardController.Register"/>).
/// when registering the control (<see cref="KeyboardController.RegisterControl"/>).
///
/// If the application doesn't implement this interface a default event handler
/// (<see cref="SIL.Windows.Forms.Keyboarding.Linux.IbusDefaultEventHandler"/>) will
Expand Down Expand Up @@ -76,7 +76,7 @@ public interface IIbusEventHandler
/// sequence.
/// </summary>
/// <param name="text">An IBusText object</param>
/// <seealso cref="IBusKeyboardAdaptor.HandleKeyPress"/>
/// <seealso cref="IbusKeyboardSwitchingAdaptor.HandleKeyPress"/>
void OnCommitText(object text);

/// <summary>
Expand All @@ -87,8 +87,7 @@ public interface IIbusEventHandler
/// <param name="cursorPos">0-based position where the cursor should be put after
/// updating the composition (pre-edit window). This position is relative to the
/// composition/preedit text.</param>
/// <param name="compositionText">An IBusText object</param>
/// <seealso cref="IBusKeyboardAdaptor.HandleKeyPress"/>
/// <seealso cref="IbusKeyboardSwitchingAdaptor.HandleKeyPress"/>
void OnUpdatePreeditText(object compositionText, int cursorPos);

/// <summary>
Expand Down Expand Up @@ -119,7 +118,7 @@ public interface IIbusEventHandler
/// depending on which modifier keys are pressed. 0 is always unmodified, and 1 is with
/// shift alone.
/// </param>
/// <seealso cref="IBusKeyboardAdaptor.HandleKeyPress"/>
/// <seealso cref="IbusKeyboardSwitchingAdaptor.HandleKeyPress"/>
void OnIbusKeyPress(int keySym, int scanCode, int modifiers);
#endregion
}
Expand Down
2 changes: 1 addition & 1 deletion SIL.Windows.Forms.Keyboarding/Linux/IbusCommunicator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public void NotifySelectionLocationAndHeight(int x, int y, int height)
/// <param name="scanCode">The X11 scan code</param>
/// <param name="state">The modifier state, i.e. shift key etc.</param>
/// <returns><c>true</c> if the key event is handled by ibus.</returns>
/// <seealso cref="IBusKeyboardAdaptor.HandleKeyPress"/>
/// <seealso cref="IbusKeyboardSwitchingAdaptor.HandleKeyPress"/>
public bool ProcessKeyEvent(int keySym, int scanCode, Keys state)
{
if (m_inputContext == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ private void CheckAttributesForCommittingKeyboard(IBusText text)
/// the composition is ending. The temporarily inserted composition string will be
/// replaced with <paramref name="ibusText"/>.
/// </summary>
/// <seealso cref="IBusKeyboardAdaptor.HandleKeyPress"/>
/// <seealso cref="IbusKeyboardSwitchingAdaptor.HandleKeyPress"/>
public void OnCommitText(object ibusText)
{
// Note: when we try to pass IBusText as ibusText parameter we get a compiler crash
Expand All @@ -189,7 +189,7 @@ public void OnCommitText(object ibusText)
/// <param name="cursorPos">0-based position where the cursor should be put after
/// updating the composition (pre-edit window). This position is relative to the
/// composition/preedit text.</param>
/// <seealso cref="IBusKeyboardAdaptor.HandleKeyPress"/>
/// <seealso cref="IbusKeyboardSwitchingAdaptor.HandleKeyPress"/>
public void OnUpdatePreeditText(object obj, int cursorPos)
{
if (m_TextBox.InvokeRequired)
Expand Down Expand Up @@ -303,7 +303,7 @@ public void OnHidePreeditText()
/// depending on which modifier keys are pressed. 0 is always unmodified, and 1 is with
/// shift alone.
/// </param>
/// <seealso cref="IBusKeyboardAdaptor.HandleKeyPress"/>
/// <seealso cref="IbusKeyboardSwitchingAdaptor.HandleKeyPress"/>
public void OnIbusKeyPress(int keySym, int scanCode, int index)
{
if (m_TextBox.InvokeRequired)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,19 @@ public bool CanHandleFormat(KeyboardFormat format)

public Action GetKeyboardSetupAction()
{
string args;
var setupApp = GetKeyboardSetupApplication(out args);
if (setupApp == null)
{
return null;
}
return () =>
{
string args;
var setupApp = GetKeyboardSetupApplication(out args);
using (Process.Start(setupApp, args)) { }
};
}

public virtual string GetKeyboardSetupApplication(out string arguments)
protected virtual string GetKeyboardSetupApplication(out string arguments)
{
arguments = null;
return File.Exists("/usr/bin/ibus-setup") ? "/usr/bin/ibus-setup" : null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2015 SIL International
// Copyright (c) 2015 SIL International
// This software is licensed under the MIT License (http://opensource.org/licenses/MIT)
using System;
using System.Collections.Generic;
Expand All @@ -20,10 +20,7 @@ public class UnityIbusKeyboardRetrievingAdaptor : IbusKeyboardRetrievingAdaptor

#region Specific implementations of IKeyboardRetriever

public override bool IsApplicable
{
get { return _helper.IsApplicable; }
}
public override bool IsApplicable => _helper.IsApplicable;

public override void Initialize()
{
Expand All @@ -32,7 +29,7 @@ public override void Initialize()
InitKeyboards();
}

public override string GetKeyboardSetupApplication(out string arguments)
protected override string GetKeyboardSetupApplication(out string arguments)
{
var program = _helper.GetKeyboardSetupApplication(out arguments);
return string.IsNullOrEmpty(program) ? base.GetKeyboardSetupApplication(out arguments) : program;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,9 +202,13 @@ public bool CanHandleFormat(KeyboardFormat format)

public Action GetKeyboardSetupAction()
{
string args;
var setupApp = GetKeyboardSetupApplication(out args);
if (setupApp == null)
{
return null;
}
return () => {
string args;
var setupApp = GetKeyboardSetupApplication(out args);
using (Process.Start(setupApp, args)) { }
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@
<Compile Include="Linux\XklEngine.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RegisterEventArgs.cs" />
<Compile Include="Windows\WindowsKeyboardSwitchingAdapter.cs" />
<Compile Include="Windows\IWindowsLanguageProfileSink.cs" />
<Compile Include="Windows\KeymanKeyboardAdaptor.cs" />
<Compile Include="Windows\KeymanKeyboardDescription.cs" />
Expand All @@ -407,7 +408,6 @@
<Compile Include="Windows\LegacyKeymanKeyboardSwitchingAdapter.cs" />
<Compile Include="Windows\MsTsfInterfaces.cs" />
<Compile Include="Windows\Win32.cs" />
<Compile Include="Windows\WindowsKeyboardSwitchingAdapter.cs" />
<Compile Include="Windows\WinKeyboardAdaptor.cs" />
<Compile Include="Windows\WinKeyboardDescription.cs" />
<Compile Include="Windows\WinKeyboardUtils.cs" />
Expand Down
96 changes: 7 additions & 89 deletions SIL.Windows.Forms.Keyboarding/Windows/KeymanKeyboardAdaptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,7 @@ public void UpdateAvailableKeyboards()
switch (InstalledKeymanVersion)
{
case KeymanVersion.Keyman10:
var keyman10 = new KeymanClass();
var langs = keyman10.Languages;
UpdateKeyboards(curKeyboards, keyman10.Keyboards.OfType<Keyman10Interop.IKeymanKeyboard>(), langs);
// Keyman10 keyboards are handled by the WinKeyboardAdapter and WindowsKeyboardSwitchingAdapter
break;
case KeymanVersion.Keyman7to9:
var keyman = new TavultesoftKeymanClass();
Expand All @@ -175,56 +173,6 @@ public void UpdateAvailableKeyboards()
#endif
}

#if !MONO
private void UpdateKeyboards(Dictionary<string, KeymanKeyboardDescription> curKeyboards, IEnumerable<Keyman10Interop.IKeymanKeyboard> availableKeyboards, Keyman10Interop.IKeymanLanguages languages)
{
var langAssocKeyboards = new Dictionary<string, string>();
foreach (Keyman10Interop.IKeymanLanguage lang in languages)
{
if (lang.KeymanKeyboardLanguage != null)
{
langAssocKeyboards[lang.LayoutName] = lang.KeymanKeyboardLanguage.BCP47Code;
}
}
foreach (Keyman10Interop.IKeymanKeyboard keyboard in availableKeyboards)
{
var keyboardName = keyboard.Name;
KeymanKeyboardDescription existingKeyboard;
if (curKeyboards.TryGetValue(keyboardName, out existingKeyboard))
{
if (!existingKeyboard.IsAvailable)
{
existingKeyboard.SetIsAvailable(true);
existingKeyboard.IsKeyman6 = false;
if (existingKeyboard.Format == KeyboardFormat.Unknown)
{
existingKeyboard.Format = KeyboardFormat.CompiledKeyman;
}
}
curKeyboards.Remove(keyboardName);
}
else
{
if (!langAssocKeyboards.ContainsKey(keyboardName))
{
continue; // a KeymanKeyboard that is not associated with a language can not be used
}
var langId = langAssocKeyboards[keyboardName];

string layout, locale;
KeyboardController.GetLayoutAndLocaleFromLanguageId(langId, out layout, out locale);

string cultureName;
var inputLanguage = WinKeyboardUtils.GetInputLanguage(locale, layout, out cultureName);
KeyboardController.Instance.Keyboards.Add(new KeymanKeyboardDescription(keyboardName, false, this, true)
{
Format = KeyboardFormat.CompiledKeyman, InputLanguage = inputLanguage
});
}
}
}
#endif

private void UpdateKeyboards(Dictionary<string, KeymanKeyboardDescription> curKeyboards, IEnumerable<string> availableKeyboardNames, bool isKeyman6)
{
foreach (string keyboardName in availableKeyboardNames)
Expand Down Expand Up @@ -281,35 +229,6 @@ public string GetKeyboardSetupApplication(out string arguments)
keyman = Path.Combine(keymanPath, @"kmshell.exe");
if (File.Exists(keyman))
{
// We would like to use the COM API for Keyman 10 but it will take an API change:
// Code that we would use:
//try
//{
// var keymanComObject = new KeymanClass();
// keymanComObject.Control.OpenConfiguration();
//}
//catch (COMException)
//{
// // Keyman is not installed
//}
// From Marc Durdin (7/16/09):
// Re LT-9902, in Keyman 6, you could launch the configuration dialog reliably by running kmshell.exe.
// However, Keyman 7 works slightly differently. The recommended approach is to use the COM API:
// http://www.tavultesoft.com/keymandev/documentation/70/comapi_interface_IKeymanProduct_OpenConfiguration.html
// Sample code:
// dim kmcom, product
// Set kmcom = CreateObject("kmcomapi.TavultesoftKeyman")
// rem Pro = ProductID 1; Light = ProductID 8
// rem Following line will raise exception if product is not installed, so try/catch it
// Set product = kmcom.Products.ItemsByProductID(1)
// Product.OpenConfiguration
// But if that is not going to be workable for you, then use the parameter "-c" to start configuration.
// Without a parameter, the action is to start Keyman Desktop itself; v7.0 would fire configuration if restarted,
// v7.1 just flags to the user that Keyman is running and where to find it. This change was due to feedback that
// users would repeatedly try to start Keyman when it was already running, and get confused when they got the
// Configuration dialog. Sorry for the unannounced change... 9
// The -c parameter will not work with Keyman 6, so you would need to test for the specific version. For what it's worth, the
// COM API is static and should not change, while the command line parameters are not guaranteed to change from version to version.
arguments = @"";
if (version > 6)
arguments = @"-c";
Expand All @@ -325,19 +244,16 @@ public string GetKeyboardSetupApplication(out string arguments)
/// </summary>
/// <param name="key">The key.</param>
/// <param name="version">The version.</param>
/// <returns></returns>
private static string GetKeymanRegistryValue(string key, ref int version)
{
using (var keyman10 = Registry.LocalMachine.OpenSubKey(@"Software\Keyman\Keyman Desktop", false))
using (var keyman6to9 = Registry.LocalMachine.OpenSubKey(@"Software\Tavultesoft\Keyman", false))
using (var keyman10_32 = Registry.LocalMachine.OpenSubKey(@"Software\WOW6432Node\Keyman\Keyman Desktop", false))
using (var keyman6to9_32 = Registry.LocalMachine.OpenSubKey(@"Software\WOW6432Node\Tavultesoft\Keyman", false))
using (var olderKeyman = Registry.LocalMachine.OpenSubKey(@"Software\Tavultesoft\Keyman", false))
using (var olderKeyman32 = Registry.LocalMachine.OpenSubKey(@"Software\WOW6432Node\Tavultesoft\Keyman", false))
{
var keymanKey = keyman10 ?? keyman6to9 ?? keyman10_32 ?? keyman6to9_32;
var keymanKey = olderKeyman32 ?? olderKeyman;
if (keymanKey == null)
return null;

int[] versions = {10, 9, 8, 7, 6, 5};
int[] versions = {9, 8, 7, 6, 5};
foreach (var vers in versions)
{
using (var rkApplication = keymanKey.OpenSubKey($"{vers}.0", false))
Expand All @@ -362,6 +278,7 @@ private static string GetKeymanRegistryValue(string key, ref int version)

public Action GetKeyboardSetupAction()
{
#if !MONO
switch (InstalledKeymanVersion)
{
case KeymanVersion.Keyman10:
Expand All @@ -380,6 +297,7 @@ public Action GetKeyboardSetupAction()
};
default:
throw new NotSupportedException($"No keyboard setup action defined for keyman version {InstalledKeymanVersion}");
#endif
}
}

Expand Down
4 changes: 2 additions & 2 deletions SIL.Windows.Forms.Keyboarding/Windows/Win32.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,9 +163,9 @@ private static Assembly MonoWinFormsAssembly
{
if (s_monoWinFormsAssembly == null)
{
#pragma warning disable 0612 // Using Obsolete method LoadWithPartialName.
#pragma warning disable 0618 // Using Obsolete method LoadWithPartialName.
s_monoWinFormsAssembly = Assembly.LoadWithPartialName("System.Windows.Forms");
#pragma warning restore 0612
#pragma warning restore 0618
}
return s_monoWinFormsAssembly;
}
Expand Down
Loading

0 comments on commit 7b774e9

Please sign in to comment.