File: Core\CSharp\System\Windows\Input\TextServicesManager.cs
Project: wpf\src\PresentationCore.csproj (PresentationCore)
//---------------------------------------------------------------------------
//
// <copyright file=TextServicesManager.cs company=Microsoft>
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
// 
//
// Description: Provides input to ImeProcessed promotion -- feeds keystrokes
//              to IMEs.
//
// History:  
//  07/23/2003 : Microsoft - Created
//
//---------------------------------------------------------------------------
 
using System.Windows.Threading;
 
using MS.Internal;
using MS.Win32;
 
using System;
using System.Security;
using System.Security.Permissions;
 
namespace System.Windows.Input
{
    internal class TextServicesManager : DispatcherObject
    {
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
 
        #region Constructors
        ///<SecurityNote> 
        /// Critical - Calls a critical method - PreProcessInput
        /// TreatAsSafe - Ok for us to register an event handler. Handler itself is critical. 
        ///</SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        internal TextServicesManager(InputManager inputManager)
        {
            _inputManager = inputManager;
 
            _inputManager.PreProcessInput += new PreProcessInputEventHandler(PreProcessInput);
            _inputManager.PostProcessInput += new ProcessInputEventHandler(PostProcessInput);
        }
 
        #endregion Constructors
 
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
 
        //------------------------------------------------------
        //
        //  Public Properties
        //
        //------------------------------------------------------
 
        //------------------------------------------------------
        //
        //  Public Events
        //
        //------------------------------------------------------
 
        //------------------------------------------------------
        //
        //  Protected Methods
        //
        //------------------------------------------------------
 
        //------------------------------------------------------
        //
        //  Internal Methods
        //
        //------------------------------------------------------
 
        // Track the focus of KeyboardDevice. KeyboardDevice.ChangeFocus() this.
        internal void Focus(DependencyObject focus)
        {
            if (focus == null)
            {
                // Don't grab keyboard events from Text Services Framework without keyboard focus.
                this.Dispatcher.IsTSFMessagePumpEnabled = false;
 
                return;
            }
 
            // Grab keyboard events from Text Services Framework with keyboard focus.
            this.Dispatcher.IsTSFMessagePumpEnabled = true;
 
            if ((bool)focus.GetValue(InputMethod.IsInputMethodSuspendedProperty))
            {
                // The focus is on the element that suspending IME's input (such as menu).
                // The document focus should remain.
                return;
            }
 
            InputMethod.Current.EnableOrDisableInputMethod((bool)focus.GetValue(InputMethod.IsInputMethodEnabledProperty));
        }
 
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
 
        //------------------------------------------------------
        //
        //  Internal Events
        //
        //------------------------------------------------------
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        #region Private Methods
 
        // Marks interesting KeyDown events as ImeInput.
        /// <SecurityNote>
        /// Critical - directly pushes keys into the input stack
        /// </SecurityNote>
        [SecurityCritical]
        private void PreProcessInput(object sender, PreProcessInputEventArgs e)
        {
            TextServicesContext context;
            KeyEventArgs keyArgs;
 
            if (!TextServicesLoader.ServicesInstalled)
                return;
 
            if(e.StagingItem.Input.RoutedEvent != Keyboard.PreviewKeyDownEvent &&
                e.StagingItem.Input.RoutedEvent != Keyboard.PreviewKeyUpEvent)
            {
                return;
            }
 
            // filter SysKey
            if (IsSysKeyDown())
                return;
 
            // IMM32-IME handles the key event and we don't do anything.
            if (InputMethod.IsImm32ImeCurrent())
                return;
 
            DependencyObject element = Keyboard.FocusedElement as DependencyObject;
            if ((element == null) || (bool)element.GetValue(InputMethod.IsInputMethodSuspendedProperty))
            {
                // The focus is on the element that suspending IME's input (such as menu).
                // we don't do anything.
                return;
            }
 
            keyArgs = (KeyEventArgs)e.StagingItem.Input;
            
            if(!keyArgs.Handled)
            {
                context = TextServicesContext.DispatcherCurrent;
 
                if (context != null)
                {
                    if (TextServicesKeystroke(context, keyArgs, true /* test */))
                    {
                        keyArgs.MarkImeProcessed();
                    }
                }
            }
        }
 
        /// <SecurityNote>
        /// Critical - directly pushes keys into the input stack
        /// </SecurityNote>
        [SecurityCritical]
        private void PostProcessInput(object sender, ProcessInputEventArgs e)
        {
            TextServicesContext context;
            KeyEventArgs keyArgs;
 
            if (!TextServicesLoader.ServicesInstalled)
                return;
 
            // IMM32-IME handles the key event and we don't do anything.
            if (InputMethod.IsImm32ImeCurrent())
                return;
 
            DependencyObject element = Keyboard.FocusedElement as DependencyObject;
            if ((element == null) || (bool)element.GetValue(InputMethod.IsInputMethodSuspendedProperty))
            {
                // The focus is on the element that suspending IME's input (such as menu).
                // we don't do anything.
                return;
            }
 
            if(e.StagingItem.Input.RoutedEvent == Keyboard.PreviewKeyDownEvent ||
               e.StagingItem.Input.RoutedEvent == Keyboard.PreviewKeyUpEvent)
            {
                // filter SysKey
                if (IsSysKeyDown())
                    return;
 
                keyArgs = (KeyEventArgs)e.StagingItem.Input;
            
                if(!keyArgs.Handled && keyArgs.Key == Key.ImeProcessed)
                {
                    context = TextServicesContext.DispatcherCurrent;
 
                    if (context != null)
                    {
                        if (TextServicesKeystroke(context, keyArgs, false /* test */))
                        {
                            keyArgs.Handled = true;
                        }
                    }
                }
            }
            else if(e.StagingItem.Input.RoutedEvent == Keyboard.KeyDownEvent ||
                    e.StagingItem.Input.RoutedEvent == Keyboard.KeyUpEvent)
            {
                keyArgs = (KeyEventArgs)e.StagingItem.Input;
                if(!keyArgs.Handled && keyArgs.Key == Key.ImeProcessed)
                {
                    keyArgs.Handled = true;
                }
            }
        }
 
        /// <SecurityNote>
        /// Critical - directly pushes keys into the input stack
        /// </SecurityNote>
        [SecurityCritical]
        private bool TextServicesKeystroke(TextServicesContext context, KeyEventArgs keyArgs, bool test)
        {
            TextServicesContext.KeyOp keyop;
            int wParam;
            int lParam;
            int scancode;
 
            // Cicero's Keystroke Manager and TIP does not recognize VK_RSHIFT or VK_LSHIFT.
            // We need to pass VK_SHIFT and the proper scancode.
            // 
            switch (keyArgs.RealKey)
            {
                case Key.RightShift:
                    wParam = NativeMethods.VK_SHIFT;
                    scancode = 0x36;
                    break;
                case Key.LeftShift:
                    wParam = NativeMethods.VK_SHIFT;
                    scancode = 0x2A;
                    break;
                default:
                    wParam = KeyInterop.VirtualKeyFromKey(keyArgs.RealKey);
                    scancode = 0; 
                    break;
            }
 
            lParam = (int)(((uint)scancode << 16) | 1);
 
            if (keyArgs.RoutedEvent == Keyboard.PreviewKeyDownEvent/*keyArgs.IsDown*/)
            {
                keyop = test ? TextServicesContext.KeyOp.TestDown : TextServicesContext.KeyOp.Down;
            }
            else
            {
                // Previous key state and transition state always 1 for WM_KEYUP.
                lParam |= (1 << 31) | (1 << 30);
 
                keyop = test ? TextServicesContext.KeyOp.TestUp : TextServicesContext.KeyOp.Up;
            }
 
            return context.Keystroke(wParam, lParam, keyop);
        }
 
        private bool IsSysKeyDown()
        {
            if (Keyboard.IsKeyDown(Key.LeftAlt) || 
                Keyboard.IsKeyDown(Key.RightAlt) ||
                Keyboard.IsKeyDown(Key.F10))
                return true;
 
            return false;
        }
 
        #endregion Private methods
 
        //------------------------------------------------------
        //
        //  Private Properties
        //
        //------------------------------------------------------
 
        //------------------------------------------------------
        //
        //  Private Fields
        //
        //------------------------------------------------------
 
        #region Private Fields
 
        ///<SecurityNote> 
        ///     Critical - required elevations to create. 
        ///</SecurityNote> 
        [SecurityCritical] 
        private readonly InputManager _inputManager;
 
        #endregion Private Fields
    }
}