/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim:expandtab:shiftwidth=2:tabstop=2: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef IMContextWrapper_h_ #define IMContextWrapper_h_ #include #include #include "nsString.h" #include "nsCOMPtr.h" #include "nsTArray.h" #include "nsIWidget.h" #include "mozilla/CheckedInt.h" #include "mozilla/ContentData.h" #include "mozilla/EventForwards.h" #include "mozilla/Maybe.h" #include "mozilla/TextEventDispatcherListener.h" #include "mozilla/WritingModes.h" #include "mozilla/GUniquePtr.h" #include "mozilla/widget/IMEData.h" class nsWindow; namespace mozilla { namespace widget { /** * KeyHandlingState is result of IMContextWrapper::OnKeyEvent(). */ enum class KeyHandlingState { // The native key event has not been handled by IMContextWrapper. eNotHandled, // The native key event was handled by IMContextWrapper. eHandled, // The native key event has not been handled by IMContextWrapper, // but eKeyDown or eKeyUp event has been dispatched. eNotHandledButEventDispatched, // The native key event has not been handled by IMContextWrapper, // but eKeyDown or eKeyUp event has been dispatched and consumed. eNotHandledButEventConsumed, }; class IMContextWrapper final : public TextEventDispatcherListener { public: // TextEventDispatcherListener implementation NS_DECL_ISUPPORTS NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher, const IMENotification& aNotification) override; NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override; NS_IMETHOD_(void) OnRemovedFrom(TextEventDispatcher* aTextEventDispatcher) override; NS_IMETHOD_(void) WillDispatchKeyboardEvent(TextEventDispatcher* aTextEventDispatcher, WidgetKeyboardEvent& aKeyboardEvent, uint32_t aIndexOfKeypress, void* aData) override; public: // aOwnerWindow is a pointer of the owner window. When aOwnerWindow is // destroyed, the related IME contexts are released (i.e., IME cannot be // used with the instance after that). explicit IMContextWrapper(nsWindow* aOwnerWindow); // Called when the process is being shut down. static void Shutdown(); // "Enabled" means the users can use all IMEs. // I.e., the focus is in the normal editors. bool IsEnabled() const; // OnFocusWindow is a notification that aWindow is going to be focused. void OnFocusWindow(nsWindow* aWindow); // OnBlurWindow is a notification that aWindow is going to be unfocused. void OnBlurWindow(nsWindow* aWindow); // OnDestroyWindow is a notification that aWindow is going to be destroyed. void OnDestroyWindow(nsWindow* aWindow); // OnFocusChangeInGecko is a notification that an editor gets focus. void OnFocusChangeInGecko(bool aFocus); // OnSelectionChange is a notification that selection (caret) is changed // in the focused editor. void OnSelectionChange(nsWindow* aCaller, const IMENotification& aIMENotification); // OnThemeChanged is called when desktop theme is changed. static void OnThemeChanged(); /** * OnKeyEvent() is called when aWindow gets a native key press event or a * native key release event. If this returns true, the key event was * filtered by IME. Otherwise, this returns false. * NOTE: When the native key press event starts composition, this returns * true but dispatches an eKeyDown event or eKeyUp event before * dispatching composition events or content command event. * * @param aWindow A window on which user operate the * key. * @param aEvent A native key press or release * event. * @param aKeyboardEventWasDispatched true if eKeyDown or eKeyUp event * for aEvent has already been * dispatched. In this case, * this class doesn't dispatch * keyboard event anymore. */ KeyHandlingState OnKeyEvent(nsWindow* aWindow, GdkEventKey* aEvent, bool aKeyboardEventWasDispatched = false); // IME related nsIWidget methods. nsresult EndIMEComposition(nsWindow* aCaller); void SetInputContext(nsWindow* aCaller, const InputContext* aContext, const InputContextAction* aAction); InputContext GetInputContext(); void OnUpdateComposition(); void OnLayoutChange(); TextEventDispatcher* GetTextEventDispatcher(); // TODO: Typically, new IM comes every several years. And now, our code // becomes really IM behavior dependent. So, perhaps, we need prefs // to control related flags for IM developers. enum class IMContextID : uint8_t { Fcitx, // 4.x or earlier Fcitx5, IBus, IIIMF, Scim, Uim, Wayland, Unknown, }; friend std::ostream& operator<<(std::ostream& aStream, const IMContextID& aIMContextID) { switch (aIMContextID) { case IMContextID::Fcitx: return aStream << "Fcitx"; case IMContextID::Fcitx5: return aStream << "Fcitx5"; case IMContextID::IBus: return aStream << "IBus"; case IMContextID::IIIMF: return aStream << "IIIMF"; case IMContextID::Scim: return aStream << "Scim"; case IMContextID::Uim: return aStream << "Uim"; case IMContextID::Wayland: return aStream << "Wayland"; case IMContextID::Unknown: return aStream << "Unknown"; } MOZ_ASSERT_UNREACHABLE("Add new case for the new IM support"); return aStream << "Unknown"; } /** * GetIMName() returns IM name associated with mContext. If the context is * xim, this look for actual engine from XMODIFIERS environment variable. */ nsDependentCSubstring GetIMName() const; /** * GetWaitingSynthesizedKeyPressHardwareKeyCode() returns hardware_keycode * value of last handled GDK_KEY_PRESS event which is probable handled by * IME asynchronously and we have not received synthesized GDK_KEY_PRESS * event yet. */ static guint16 GetWaitingSynthesizedKeyPressHardwareKeyCode() { return sWaitingSynthesizedKeyPressHardwareKeyCode; } protected: ~IMContextWrapper(); /** * SetInputPurposeAndInputHints() sets input-purpose and input-hints of * current IM context to the values computed with mInputContext. */ void SetInputPurposeAndInputHints(); // Owner of an instance of this class. This should be top level window. // The owner window must release the contexts when it's destroyed because // the IME contexts need the native window. If OnDestroyWindow() is called // with the owner window, it'll release IME contexts. Otherwise, it'll // just clean up any existing composition if it's related to the destroying // child window. nsWindow* mOwnerWindow; // A last focused window in this class's context. nsWindow* mLastFocusedWindow; // Actual context. This is used for handling the user's input. GtkIMContext* mContext; // mSimpleContext is used for the password field and // the |ime-mode: disabled;| editors if sUseSimpleContext is true. // These editors disable IME. But dead keys should work. Fortunately, // the simple IM context of GTK2 support only them. GtkIMContext* mSimpleContext; // mDummyContext is a dummy context and will be used in Focus() // when the state of mEnabled means disabled. This context's IME state is // always "closed", so it closes IME forcedly. GtkIMContext* mDummyContext; // mComposingContext is not nullptr while one of mContext, mSimpleContext // and mDummyContext has composition. // XXX: We don't assume that two or more context have composition same time. GtkIMContext* mComposingContext; // IME enabled state and other things defined in InputContext. // Use following helper methods if you don't need the detail of the status. InputContext mInputContext; // mCompositionStart is the start offset of the composition string in the // current content. When