Files
LuaCsForBarotraumaEP/Libraries/ImeSharp/TextServicesContext.cs
2022-11-14 18:28:28 +02:00

375 lines
12 KiB
C#

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Diagnostics;
using ImeSharp.Native;
using TsfSharp;
namespace ImeSharp
{
//------------------------------------------------------
//
// TextServicesContext class
//
//------------------------------------------------------
/// <summary>
/// This class manages the ITfThreadMgr, EmptyDim and the reference to
/// the default TextStore.
/// </summary>
/// <remarks>
/// </remarks>
internal class TextServicesContext
{
public const int TF_POPF_ALL = 0x0001;
public const int TF_INVALID_COOKIE = -1;
public static readonly Guid IID_ITfUIElementSink = new Guid(0xea1ea136, 0x19df, 0x11d7, 0xa6, 0xd2, 0x00, 0x06, 0x5b, 0x84, 0x43, 0x5c);
public static readonly Guid IID_ITfTextEditSink = new Guid(0x8127d409, 0xccd3, 0x4683, 0x96, 0x7a, 0xb4, 0x3d, 0x5b, 0x48, 0x2b, 0xf7);
public static TextServicesContext Current
{
get
{
if (InputMethod.TextServicesContext == null)
InputMethod.TextServicesContext = new TextServicesContext();
return InputMethod.TextServicesContext;
}
}
//------------------------------------------------------
//
// Constructors
//
//------------------------------------------------------
#region Constructors
/// <summary>
/// Instantiates a TextServicesContext.
/// </summary>
private TextServicesContext()
{
if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA)
Debug.WriteLine("CRASH: ImeSharp won't work on MTA thread!!!");
}
#endregion Constructors
//------------------------------------------------------
//
// public Methods
//
//------------------------------------------------------
#region public Methods
/// <summary>
/// Releases all unmanaged resources allocated by the
/// TextServicesContext.
/// </summary>
/// <remarks>
/// if appDomainShutdown == false, this method must be called on the
/// Dispatcher thread. Otherwise, the caller is an AppDomain.Shutdown
/// listener, and is calling from a worker thread.
/// </remarks>
public void Uninitialize(bool appDomainShutdown)
{
// Unregister DefaultTextStore.
if (_defaultTextStore != null)
{
UnadviseSinks();
if (_defaultTextStore.DocumentManager != null)
{
_defaultTextStore.DocumentManager.Pop(TF_POPF_ALL);
_defaultTextStore.DocumentManager.Dispose();
_defaultTextStore.DocumentManager = null;
}
_defaultTextStore = null;
}
// Free up any remaining textstores.
if (_istimactivated == true)
{
// Shut down the thread manager when the last TextStore goes away.
// On XP, if we're called on a worker thread (during AppDomain shutdown)
// we can't call call any methods on _threadManager. The problem is
// that there's no proxy registered for ITfThreadMgr on OS versions
// previous to Vista. Not calling Deactivate will leak the IMEs, but
// in practice (1) they're singletons, so it's not unbounded; and (2)
// most applications will share the thread with other AppDomains that
// have a UI, in which case the IME won't be released until the process
// shuts down in any case. In theory we could also work around this
// problem by creating our own XP proxy/stub implementation, which would
// be added to WPF setup....
if (!appDomainShutdown || System.Environment.OSVersion.Version.Major >= 6)
{
_threadManager.Deactivate();
}
_istimactivated = false;
}
// Release the empty dim.
if (_dimEmpty != null)
{
if (_dimEmpty != null)
{
_dimEmpty.Dispose();
}
_dimEmpty = null;
}
// Release the ThreadManager.
// We don't do this in UnregisterTextStore because someone may have
// called get_ThreadManager after the last TextStore was unregistered.
if (_threadManager != null)
{
if (_threadManager != null)
{
_threadManager.Dispose();
}
_threadManager = null;
}
}
// Called by framework's TextStore class. This method registers a
// document with TSF. The TextServicesContext must maintain this list
// to ensure all native resources are released after gc or uninitialization.
public void RegisterTextStore(TextStore defaultTextStore)
{
_defaultTextStore = defaultTextStore;
ITfThreadMgrEx threadManager = ThreadManager;
if (threadManager != null)
{
ITfDocumentMgr doc;
int editCookie = TF_INVALID_COOKIE;
// Activate TSF on this thread if this is the first TextStore.
if (_istimactivated == false)
{
//temp variable created to retrieve the value
// which is then stored in the critical data.
if (InputMethod.ShowOSImeWindow)
_clientId = threadManager.Activate();
else
_clientId = threadManager.ActivateEx(TfTmaeFlags.Uielementenabledonly);
_istimactivated = true;
}
// Create a TSF document.
doc = threadManager.CreateDocumentMgr();
_defaultTextStore.DocumentManager = doc;
doc.CreateContext(_clientId, 0 /* flags */, _defaultTextStore, out _editContext, out editCookie);
_defaultTextStore.EditCookie = editCookie;
_contextOwnerServices = _editContext.QueryInterface<ITfContextOwnerServices>();
doc.Push(_editContext);
AdviseSinks();
}
}
public void SetFocusOnDefaultTextStore()
{
SetFocusOnDim(TextStore.Current.DocumentManager);
}
public void SetFocusOnEmptyDim()
{
SetFocusOnDim(EmptyDocumentManager);
}
#endregion public Methods
//------------------------------------------------------
//
// public Properties
//
//------------------------------------------------------
/// <summary>
/// The default ITfThreadMgrEx object.
/// </summary>
public ITfThreadMgrEx ThreadManager
{
// The ITfThreadMgr for this thread.
get
{
if (_threadManager == null)
{
ITfThreadMgr threadMgr = null;
try
{
// This might fail in CoreRT
threadMgr = Tsf.GetThreadMgr();
}
catch (SharpGen.Runtime.SharpGenException)
{
threadMgr = null;
}
// Dispose previous ITfThreadMgr in case something weird happens
if (threadMgr != null)
{
if (threadMgr.IsThreadFocus)
threadMgr.Deactivate();
threadMgr.Dispose();
}
_threadManager = TextServicesLoader.Load();
_uiElementMgr = _threadManager.QueryInterface<ITfUIElementMgr>();
}
return _threadManager;
}
}
/// <summary>
/// Return the created ITfContext object.
/// </summary>
public ITfContext EditContext
{
get { return _editContext; }
}
/// <summary>
/// Return the created ITfUIElementMgr object.
/// </summary>
public ITfUIElementMgr UIElementMgr
{
get { return _uiElementMgr; }
}
/// <summary>
/// Return the created ITfContextOwnerServices object.
/// </summary>
public ITfContextOwnerServices ContextOwnerServices
{
get { return _contextOwnerServices; }
}
//------------------------------------------------------
//
// public Events
//
//------------------------------------------------------
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
private void SetFocusOnDim(ITfDocumentMgr dim)
{
ITfThreadMgrEx threadmgr = ThreadManager;
if (threadmgr != null)
{
ITfDocumentMgr prevDocMgr = threadmgr.AssociateFocus(InputMethod.WindowHandle, dim);
}
}
private void AdviseSinks()
{
var source = _uiElementMgr.QueryInterface<ITfSource>();
var guid = IID_ITfUIElementSink;
int sinkCookie = source.AdviseSink(guid, _defaultTextStore);
_defaultTextStore.UIElementSinkCookie = sinkCookie;
source.Dispose();
source = _editContext.QueryInterface<ITfSource>();
guid = IID_ITfTextEditSink;
sinkCookie = source.AdviseSink(guid, _defaultTextStore);
_defaultTextStore.TextEditSinkCookie = sinkCookie;
source.Dispose();
}
private void UnadviseSinks()
{
var source = _uiElementMgr.QueryInterface<ITfSource>();
if (_defaultTextStore.UIElementSinkCookie != TF_INVALID_COOKIE)
{
source.UnadviseSink(_defaultTextStore.UIElementSinkCookie);
_defaultTextStore.UIElementSinkCookie = TF_INVALID_COOKIE;
}
source.Dispose();
source = _editContext.QueryInterface<ITfSource>();
if (_defaultTextStore.TextEditSinkCookie != TF_INVALID_COOKIE)
{
source.UnadviseSink(_defaultTextStore.TextEditSinkCookie);
_defaultTextStore.TextEditSinkCookie = TF_INVALID_COOKIE;
}
source.Dispose();
}
//------------------------------------------------------
//
// Private Properties
//
//------------------------------------------------------
// Create an empty dim on demand.
private ITfDocumentMgr EmptyDocumentManager
{
get
{
if (_dimEmpty == null)
{
ITfThreadMgrEx threadManager = ThreadManager;
if (threadManager == null)
{
return null;
}
ITfDocumentMgr dimEmptyTemp;
// Create a TSF document.
dimEmptyTemp = threadManager.CreateDocumentMgr();
_dimEmpty = dimEmptyTemp;
}
return _dimEmpty;
}
}
//------------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
#region Private Fields
private TextStore _defaultTextStore;
private ITfContext _editContext;
private ITfUIElementMgr _uiElementMgr;
private ITfContextOwnerServices _contextOwnerServices;
// This is true if thread manager is activated.
private bool _istimactivated;
// The root TSF object, created on demand.
private ITfThreadMgrEx _threadManager;
// TSF ClientId from Activate call.
private int _clientId;
// The empty dim for this thread. Created on demand.
private ITfDocumentMgr _dimEmpty;
#endregion Private Fields
}
}