From 2aa4a82499d4becd2284cdb482213d541b8804dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 16:29:10 +0200 Subject: Adding upstream version 86.0.1. Signed-off-by: Daniel Baumann --- widget/gtk/IMContextWrapper.h | 685 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 685 insertions(+) create mode 100644 widget/gtk/IMContextWrapper.h (limited to 'widget/gtk/IMContextWrapper.h') diff --git a/widget/gtk/IMContextWrapper.h b/widget/gtk/IMContextWrapper.h new file mode 100644 index 0000000000..9ebb22fc3d --- /dev/null +++ b/widget/gtk/IMContextWrapper.h @@ -0,0 +1,685 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* 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/EventForwards.h" +#include "mozilla/TextEventDispatcherListener.h" +#include "WritingModes.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(); + + // 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