diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8fbb517e..abdcd967 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -261,6 +261,7 @@ protocol("unstable/text-input/text-input-unstable-v1.xml" "text-input-unstable-v
protocolNew("protocols/wlr-gamma-control-unstable-v1.xml" "wlr-gamma-control-unstable-v1" true)
protocolNew("protocols/wlr-foreign-toplevel-management-unstable-v1.xml" "wlr-foreign-toplevel-management-unstable-v1" true)
protocolNew("protocols/wlr-output-power-management-unstable-v1.xml" "wlr-output-power-management-unstable-v1" true)
+protocolNew("protocols/input-method-unstable-v2.xml" "input-method-unstable-v2" true)
protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false)
protocolNew("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false)
protocolNew("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false)
diff --git a/protocols/input-method-unstable-v2.xml b/protocols/input-method-unstable-v2.xml
new file mode 100644
index 00000000..51bccf28
--- /dev/null
+++ b/protocols/input-method-unstable-v2.xml
@@ -0,0 +1,494 @@
+
+
+
+
+ Copyright © 2008-2011 Kristian Høgsberg
+ Copyright © 2010-2011 Intel Corporation
+ Copyright © 2012-2013 Collabora, Ltd.
+ Copyright © 2012, 2013 Intel Corporation
+ Copyright © 2015, 2016 Jan Arne Petersen
+ Copyright © 2017, 2018 Red Hat, Inc.
+ Copyright © 2018 Purism SPC
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+ This protocol allows applications to act as input methods for compositors.
+
+ An input method context is used to manage the state of the input method.
+
+ Text strings are UTF-8 encoded, their indices and lengths are in bytes.
+
+ This document adheres to the RFC 2119 when using words like "must",
+ "should", "may", etc.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+
+
+
+
+ An input method object allows for clients to compose text.
+
+ The objects connects the client to a text input in an application, and
+ lets the client to serve as an input method for a seat.
+
+ The zwp_input_method_v2 object can occupy two distinct states: active and
+ inactive. In the active state, the object is associated to and
+ communicates with a text input. In the inactive state, there is no
+ associated text input, and the only communication is with the compositor.
+ Initially, the input method is in the inactive state.
+
+ Requests issued in the inactive state must be accepted by the compositor.
+ Because of the serial mechanism, and the state reset on activate event,
+ they will not have any effect on the state of the next text input.
+
+ There must be no more than one input method object per seat.
+
+
+
+
+
+
+
+
+ Notification that a text input focused on this seat requested the input
+ method to be activated.
+
+ This event serves the purpose of providing the compositor with an
+ active input method.
+
+ This event resets all state associated with previous enable, disable,
+ surrounding_text, text_change_cause, and content_type events, as well
+ as the state associated with set_preedit_string, commit_string, and
+ delete_surrounding_text requests. In addition, it marks the
+ zwp_input_method_v2 object as active, and makes any existing
+ zwp_input_popup_surface_v2 objects visible.
+
+ The surrounding_text, and content_type events must follow before the
+ next done event if the text input supports the respective
+ functionality.
+
+ State set with this event is double-buffered. It will get applied on
+ the next zwp_input_method_v2.done event, and stay valid until changed.
+
+
+
+
+
+ Notification that no focused text input currently needs an active
+ input method on this seat.
+
+ This event marks the zwp_input_method_v2 object as inactive. The
+ compositor must make all existing zwp_input_popup_surface_v2 objects
+ invisible until the next activate event.
+
+ State set with this event is double-buffered. It will get applied on
+ the next zwp_input_method_v2.done event, and stay valid until changed.
+
+
+
+
+
+ Updates the surrounding plain text around the cursor, excluding the
+ preedit text.
+
+ If any preedit text is present, it is replaced with the cursor for the
+ purpose of this event.
+
+ The argument text is a buffer containing the preedit string, and must
+ include the cursor position, and the complete selection. It should
+ contain additional characters before and after these. There is a
+ maximum length of wayland messages, so text can not be longer than 4000
+ bytes.
+
+ cursor is the byte offset of the cursor within the text buffer.
+
+ anchor is the byte offset of the selection anchor within the text
+ buffer. If there is no selected text, anchor must be the same as
+ cursor.
+
+ If this event does not arrive before the first done event, the input
+ method may assume that the text input does not support this
+ functionality and ignore following surrounding_text events.
+
+ Values set with this event are double-buffered. They will get applied
+ and set to initial values on the next zwp_input_method_v2.done
+ event.
+
+ The initial state for affected fields is empty, meaning that the text
+ input does not support sending surrounding text. If the empty values
+ get applied, subsequent attempts to change them may have no effect.
+
+
+
+
+
+
+
+
+ Tells the input method why the text surrounding the cursor changed.
+
+ Whenever the client detects an external change in text, cursor, or
+ anchor position, it must issue this request to the compositor. This
+ request is intended to give the input method a chance to update the
+ preedit text in an appropriate way, e.g. by removing it when the user
+ starts typing with a keyboard.
+
+ cause describes the source of the change.
+
+ The value set with this event is double-buffered. It will get applied
+ and set to its initial value on the next zwp_input_method_v2.done
+ event.
+
+ The initial value of cause is input_method.
+
+
+
+
+
+
+ Indicates the content type and hint for the current
+ zwp_input_method_v2 instance.
+
+ Values set with this event are double-buffered. They will get applied
+ on the next zwp_input_method_v2.done event.
+
+ The initial value for hint is none, and the initial value for purpose
+ is normal.
+
+
+
+
+
+
+
+ Atomically applies state changes recently sent to the client.
+
+ The done event establishes and updates the state of the client, and
+ must be issued after any changes to apply them.
+
+ Text input state (content purpose, content hint, surrounding text, and
+ change cause) is conceptually double-buffered within an input method
+ context.
+
+ Events modify the pending state, as opposed to the current state in use
+ by the input method. A done event atomically applies all pending state,
+ replacing the current state. After done, the new pending state is as
+ documented for each related request.
+
+ Events must be applied in the order of arrival.
+
+ Neither current nor pending state are modified unless noted otherwise.
+
+
+
+
+
+ Send the commit string text for insertion to the application.
+
+ Inserts a string at current cursor position (see commit event
+ sequence). The string to commit could be either just a single character
+ after a key press or the result of some composing.
+
+ The argument text is a buffer containing the string to insert. There is
+ a maximum length of wayland messages, so text can not be longer than
+ 4000 bytes.
+
+ Values set with this event are double-buffered. They must be applied
+ and reset to initial on the next zwp_text_input_v3.commit request.
+
+ The initial value of text is an empty string.
+
+
+
+
+
+
+ Send the pre-edit string text to the application text input.
+
+ Place a new composing text (pre-edit) at the current cursor position.
+ Any previously set composing text must be removed. Any previously
+ existing selected text must be removed. The cursor is moved to a new
+ position within the preedit string.
+
+ The argument text is a buffer containing the preedit string. There is
+ a maximum length of wayland messages, so text can not be longer than
+ 4000 bytes.
+
+ The arguments cursor_begin and cursor_end are counted in bytes relative
+ to the beginning of the submitted string buffer. Cursor should be
+ hidden by the text input when both are equal to -1.
+
+ cursor_begin indicates the beginning of the cursor. cursor_end
+ indicates the end of the cursor. It may be equal or different than
+ cursor_begin.
+
+ Values set with this event are double-buffered. They must be applied on
+ the next zwp_input_method_v2.commit event.
+
+ The initial value of text is an empty string. The initial value of
+ cursor_begin, and cursor_end are both 0.
+
+
+
+
+
+
+
+
+ Remove the surrounding text.
+
+ before_length and after_length are the number of bytes before and after
+ the current cursor index (excluding the preedit text) to delete.
+
+ If any preedit text is present, it is replaced with the cursor for the
+ purpose of this event. In effect before_length is counted from the
+ beginning of preedit text, and after_length from its end (see commit
+ event sequence).
+
+ Values set with this event are double-buffered. They must be applied
+ and reset to initial on the next zwp_input_method_v2.commit request.
+
+ The initial values of both before_length and after_length are 0.
+
+
+
+
+
+
+
+ Apply state changes from commit_string, set_preedit_string and
+ delete_surrounding_text requests.
+
+ The state relating to these events is double-buffered, and each one
+ modifies the pending state. This request replaces the current state
+ with the pending state.
+
+ The connected text input is expected to proceed by evaluating the
+ changes in the following order:
+
+ 1. Replace existing preedit string with the cursor.
+ 2. Delete requested surrounding text.
+ 3. Insert commit string with the cursor at its end.
+ 4. Calculate surrounding text to send.
+ 5. Insert new preedit text in cursor position.
+ 6. Place cursor inside preedit text.
+
+ The serial number reflects the last state of the zwp_input_method_v2
+ object known to the client. The value of the serial argument must be
+ equal to the number of done events already issued by that object. When
+ the compositor receives a commit request with a serial different than
+ the number of past done events, it must proceed as normal, except it
+ should not change the current state of the zwp_input_method_v2 object.
+
+
+
+
+
+
+ Creates a new zwp_input_popup_surface_v2 object wrapping a given
+ surface.
+
+ The surface gets assigned the "input_popup" role. If the surface
+ already has an assigned role, the compositor must issue a protocol
+ error.
+
+
+
+
+
+
+
+ Allow an input method to receive hardware keyboard input and process
+ key events to generate text events (with pre-edit) over the wire. This
+ allows input methods which compose multiple key events for inputting
+ text like it is done for CJK languages.
+
+ The compositor should send all keyboard events on the seat to the grab
+ holder via the returned wl_keyboard object. Nevertheless, the
+ compositor may decide not to forward any particular event. The
+ compositor must not further process any event after it has been
+ forwarded to the grab holder.
+
+ Releasing the resulting wl_keyboard object releases the grab.
+
+
+
+
+
+
+ The input method ceased to be available.
+
+ The compositor must issue this event as the only event on the object if
+ there was another input_method object associated with the same seat at
+ the time of its creation.
+
+ The compositor must issue this request when the object is no longer
+ usable, e.g. due to seat removal.
+
+ The input method context becomes inert and should be destroyed after
+ deactivation is handled. Any further requests and events except for the
+ destroy request must be ignored.
+
+
+
+
+
+ Destroys the zwp_text_input_v2 object and any associated child
+ objects, i.e. zwp_input_popup_surface_v2 and
+ zwp_input_method_keyboard_grab_v2.
+
+
+
+
+
+
+ This interface marks a surface as a popup for interacting with an input
+ method.
+
+ The compositor should place it near the active text input area. It must
+ be visible if and only if the input method is in the active state.
+
+ The client must not destroy the underlying wl_surface while the
+ zwp_input_popup_surface_v2 object exists.
+
+
+
+
+ Notify about the position of the area of the text input expressed as a
+ rectangle in surface local coordinates.
+
+ This is a hint to the input method telling it the relative position of
+ the text being entered.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The zwp_input_method_keyboard_grab_v2 interface represents an exclusive
+ grab of the wl_keyboard interface associated with the seat.
+
+
+
+
+ This event provides a file descriptor to the client which can be
+ memory-mapped to provide a keyboard mapping description.
+
+
+
+
+
+
+
+
+ A key was pressed or released.
+ The time argument is a timestamp with millisecond granularity, with an
+ undefined base.
+
+
+
+
+
+
+
+
+
+ Notifies clients that the modifier and/or group state has changed, and
+ it should update its local state.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Informs the client about the keyboard's repeat rate and delay.
+
+ This event is sent as soon as the zwp_input_method_keyboard_grab_v2
+ object has been created, and is guaranteed to be received by the
+ client before any key press event.
+
+ Negative values for either rate or delay are illegal. A rate of zero
+ will disable any repeating (regardless of the value of delay).
+
+ This event can be sent later on as well with a new value if necessary,
+ so clients should continue listening for the event past the creation
+ of zwp_input_method_keyboard_grab_v2.
+
+
+
+
+
+
+
+
+ The input method manager allows the client to become the input method on
+ a chosen seat.
+
+ No more than one input method must be associated with any seat at any
+ given time.
+
+
+
+
+ Request a new input zwp_input_method_v2 object associated with a given
+ seat.
+
+
+
+
+
+
+
+ Destroys the zwp_input_method_manager_v2 object.
+
+ The zwp_input_method_v2 objects originating from it remain valid.
+
+
+
+
diff --git a/protocols/meson.build b/protocols/meson.build
index a0728e39..4017ff27 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -38,6 +38,7 @@ new_protocols = [
['wlr-gamma-control-unstable-v1.xml'],
['wlr-foreign-toplevel-management-unstable-v1.xml'],
['wlr-output-power-management-unstable-v1.xml'],
+ ['input-method-unstable-v2.xml'],
[wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'],
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
diff --git a/src/Compositor.cpp b/src/Compositor.cpp
index a4647afe..9ed0ea38 100644
--- a/src/Compositor.cpp
+++ b/src/Compositor.cpp
@@ -262,8 +262,6 @@ void CCompositor::initServer() {
wlr_xdg_foreign_v1_create(m_sWLDisplay, m_sWLRForeignRegistry);
wlr_xdg_foreign_v2_create(m_sWLDisplay, m_sWLRForeignRegistry);
- m_sWLRIMEMgr = wlr_input_method_manager_v2_create(m_sWLDisplay);
-
m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLEventLoop);
if (!m_sWLRHeadlessBackend) {
@@ -312,7 +310,6 @@ void CCompositor::initAllSignals() {
addWLSignal(&m_sWLRVirtPtrMgr->events.new_virtual_pointer, &Events::listen_newVirtPtr, m_sWLRVirtPtrMgr, "VirtPtrMgr");
addWLSignal(&m_sWLRVKeyboardMgr->events.new_virtual_keyboard, &Events::listen_newVirtualKeyboard, m_sWLRVKeyboardMgr, "VKeyboardMgr");
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
- addWLSignal(&m_sWLRIMEMgr->events.input_method, &Events::listen_newIME, m_sWLRIMEMgr, "IMEMgr");
if (m_sWRLDRMLeaseMgr)
addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM");
@@ -355,7 +352,6 @@ void CCompositor::removeAllSignals() {
removeWLSignal(&Events::listen_newVirtPtr);
removeWLSignal(&Events::listen_newVirtualKeyboard);
removeWLSignal(&Events::listen_RendererDestroy);
- removeWLSignal(&Events::listen_newIME);
if (m_sWRLDRMLeaseMgr)
removeWLSignal(&Events::listen_leaseRequest);
diff --git a/src/Compositor.hpp b/src/Compositor.hpp
index 3201b28f..b867742b 100644
--- a/src/Compositor.hpp
+++ b/src/Compositor.hpp
@@ -64,7 +64,6 @@ class CCompositor {
wlr_virtual_pointer_manager_v1* m_sWLRVirtPtrMgr;
wlr_tablet_manager_v2* m_sWLRTabletManager;
wlr_xdg_foreign_registry* m_sWLRForeignRegistry;
- wlr_input_method_manager_v2* m_sWLRIMEMgr;
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
wlr_backend* m_sWLRHeadlessBackend;
// ------------------------------------------------- //
diff --git a/src/events/Events.hpp b/src/events/Events.hpp
index f147c6bf..2dabac11 100644
--- a/src/events/Events.hpp
+++ b/src/events/Events.hpp
@@ -107,7 +107,6 @@ namespace Events {
LISTENER(pinchEnd);
// IME
- LISTENER(newIME);
LISTENER(newVirtualKeyboard);
// Touch
diff --git a/src/events/Misc.cpp b/src/events/Misc.cpp
index e5ead5ff..13dc85a3 100644
--- a/src/events/Misc.cpp
+++ b/src/events/Misc.cpp
@@ -187,9 +187,3 @@ void Events::listener_sessionActive(wl_listener* listener, void* data) {
g_pConfigManager->m_bWantsMonitorReload = true;
}
-
-void Events::listener_newIME(wl_listener* listener, void* data) {
- Debug::log(LOG, "New IME added!");
-
- g_pInputManager->m_sIMERelay.onNewIME((wlr_input_method_v2*)data);
-}
diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp
index b8061bef..22aa48f4 100644
--- a/src/helpers/MiscFunctions.cpp
+++ b/src/helpers/MiscFunctions.cpp
@@ -2,9 +2,12 @@
#include "../defines.hpp"
#include
#include "../Compositor.hpp"
+#include "../managers/TokenManager.hpp"
#include
#include
#include
+#include
+#include
#include
#include
#ifdef HAS_EXECINFO
@@ -834,3 +837,35 @@ bool envEnabled(const std::string& env) {
return false;
return std::string(ENV) == "1";
}
+
+std::pair openExclusiveShm() {
+ std::string name = g_pTokenManager->getRandomUUID();
+
+ for (size_t i = 0; i < 69; ++i) {
+ int fd = shm_open(name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
+ if (fd >= 0)
+ return {fd, name};
+ }
+
+ return {-1, ""};
+}
+
+int allocateSHMFile(size_t len) {
+ auto [fd, name] = openExclusiveShm();
+ if (fd < 0)
+ return -1;
+
+ shm_unlink(name.c_str());
+
+ int ret;
+ do {
+ ret = ftruncate(fd, len);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp
index d8a86b95..80103eac 100644
--- a/src/helpers/MiscFunctions.hpp
+++ b/src/helpers/MiscFunctions.hpp
@@ -38,6 +38,7 @@ void throwError(const std::string& err);
uint32_t drmFormatToGL(uint32_t drm);
uint32_t glFormatToType(uint32_t gl);
bool envEnabled(const std::string& env);
+int allocateSHMFile(size_t len);
template
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string fmt, Args&&... args) {
diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp
index 024ac38d..2f14059b 100644
--- a/src/helpers/WLClasses.hpp
+++ b/src/helpers/WLClasses.hpp
@@ -218,14 +218,6 @@ struct SSwipeGesture {
CMonitor* pMonitor = nullptr;
};
-struct SIMEKbGrab {
- wlr_input_method_keyboard_grab_v2* pWlrKbGrab = nullptr;
-
- wlr_keyboard* pKeyboard = nullptr;
-
- DYNLISTENER(grabDestroy);
-};
-
struct STouchDevice {
wlr_input_device* pWlrDevice = nullptr;
diff --git a/src/includes.hpp b/src/includes.hpp
index ed40331f..15d5c1aa 100644
--- a/src/includes.hpp
+++ b/src/includes.hpp
@@ -83,7 +83,6 @@ extern "C" {
#include
#include
#include
-#include
#include
#include
#include
diff --git a/src/managers/KeybindManager.cpp b/src/managers/KeybindManager.cpp
index ef74bf5c..1150ac2e 100644
--- a/src/managers/KeybindManager.cpp
+++ b/src/managers/KeybindManager.cpp
@@ -324,9 +324,6 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard
return true;
}
- if (pKeyboard->isVirtual && g_pInputManager->shouldIgnoreVirtualKeyboard(pKeyboard))
- return true;
-
if (!m_pXKBTranslationState) {
Debug::log(ERR, "BUG THIS: m_pXKBTranslationState NULL!");
updateXKBTranslationState();
diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp
index 580622b0..09ca5206 100644
--- a/src/managers/ProtocolManager.cpp
+++ b/src/managers/ProtocolManager.cpp
@@ -19,26 +19,7 @@
#include "../protocols/XDGActivation.hpp"
#include "../protocols/IdleNotify.hpp"
#include "../protocols/SessionLock.hpp"
-
-#include "tearing-control-v1.hpp"
-#include "fractional-scale-v1.hpp"
-#include "xdg-output-unstable-v1.hpp"
-#include "cursor-shape-v1.hpp"
-#include "idle-inhibit-unstable-v1.hpp"
-#include "relative-pointer-unstable-v1.hpp"
-#include "xdg-decoration-unstable-v1.hpp"
-#include "alpha-modifier-v1.hpp"
-#include "wlr-gamma-control-unstable-v1.hpp"
-#include "ext-foreign-toplevel-list-v1.hpp"
-#include "pointer-gestures-unstable-v1.hpp"
-#include "wlr-foreign-toplevel-management-unstable-v1.hpp"
-#include "keyboard-shortcuts-inhibit-unstable-v1.hpp"
-#include "text-input-unstable-v3.hpp"
-#include "pointer-constraints-unstable-v1.hpp"
-#include "wlr-output-power-management-unstable-v1.hpp"
-#include "xdg-activation-v1.hpp"
-#include "ext-idle-notify-v1.hpp"
-#include "ext-session-lock-v1.hpp"
+#include "../protocols/InputMethodV2.hpp"
CProtocolManager::CProtocolManager() {
@@ -61,6 +42,7 @@ CProtocolManager::CProtocolManager() {
PROTO::activation = std::make_unique(&xdg_activation_v1_interface, 1, "XDGActivation");
PROTO::idle = std::make_unique(&ext_idle_notifier_v1_interface, 1, "IdleNotify");
PROTO::sessionLock = std::make_unique(&ext_session_lock_manager_v1_interface, 1, "SessionLock");
+ PROTO::ime = std::make_unique(&zwp_input_method_manager_v2_interface, 1, "IMEv2");
// Old protocol implementations.
// TODO: rewrite them to use hyprwayland-scanner.
diff --git a/src/managers/input/InputManager.cpp b/src/managers/input/InputManager.cpp
index ca1597b1..7d14719f 100644
--- a/src/managers/input/InputManager.cpp
+++ b/src/managers/input/InputManager.cpp
@@ -10,6 +10,7 @@
#include "../../protocols/PointerConstraints.hpp"
#include "../../protocols/IdleNotify.hpp"
#include "../../protocols/SessionLock.hpp"
+#include "../../protocols/InputMethodV2.hpp"
CInputManager::CInputManager() {
m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) {
@@ -1207,6 +1208,8 @@ void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboar
if (!pKeyboard->enabled)
return;
+ const bool DISALLOWACTION = pKeyboard->isVirtual && shouldIgnoreVirtualKeyboard(pKeyboard);
+
const auto EMAP = std::unordered_map{{"keyboard", pKeyboard}, {"event", e}};
EMIT_HOOK_EVENT_CANCELLABLE("keyPress", EMAP);
@@ -1216,17 +1219,16 @@ void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboar
g_pKeybindManager->dpms("on");
}
- bool passEvent = g_pKeybindManager->onKeyEvent(e, pKeyboard);
+ bool passEvent = DISALLOWACTION || g_pKeybindManager->onKeyEvent(e, pKeyboard);
PROTO::idle->onActivity();
if (passEvent) {
+ const auto IME = m_sIMERelay.m_pIME.lock();
- const auto PIMEGRAB = m_sIMERelay.getIMEKeyboardGrab(pKeyboard);
-
- if (PIMEGRAB && PIMEGRAB->pWlrKbGrab && PIMEGRAB->pWlrKbGrab->input_method) {
- wlr_input_method_keyboard_grab_v2_set_keyboard(PIMEGRAB->pWlrKbGrab, wlr_keyboard_from_input_device(pKeyboard->keyboard));
- wlr_input_method_keyboard_grab_v2_send_key(PIMEGRAB->pWlrKbGrab, e->time_msec, e->keycode, e->state);
+ if (IME && IME->hasGrab() && !DISALLOWACTION) {
+ IME->setKeyboard(wlr_keyboard_from_input_device(pKeyboard->keyboard));
+ IME->sendKey(e->time_msec, e->keycode, e->state);
} else {
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(pKeyboard->keyboard));
wlr_seat_keyboard_notify_key(g_pCompositor->m_sSeat.seat, e->time_msec, e->keycode, e->state);
@@ -1240,16 +1242,18 @@ void CInputManager::onKeyboardMod(void* data, SKeyboard* pKeyboard) {
if (!pKeyboard->enabled)
return;
- const auto PIMEGRAB = m_sIMERelay.getIMEKeyboardGrab(pKeyboard);
+ const bool DISALLOWACTION = pKeyboard->isVirtual && shouldIgnoreVirtualKeyboard(pKeyboard);
const auto ALLMODS = accumulateModsFromAllKBs();
auto MODS = wlr_keyboard_from_input_device(pKeyboard->keyboard)->modifiers;
MODS.depressed = ALLMODS;
- if (PIMEGRAB && PIMEGRAB->pWlrKbGrab && PIMEGRAB->pWlrKbGrab->input_method) {
- wlr_input_method_keyboard_grab_v2_set_keyboard(PIMEGRAB->pWlrKbGrab, wlr_keyboard_from_input_device(pKeyboard->keyboard));
- wlr_input_method_keyboard_grab_v2_send_modifiers(PIMEGRAB->pWlrKbGrab, &MODS);
+ const auto IME = m_sIMERelay.m_pIME.lock();
+
+ if (IME && IME->hasGrab() && !DISALLOWACTION) {
+ IME->setKeyboard(wlr_keyboard_from_input_device(pKeyboard->keyboard));
+ IME->sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group);
} else {
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(pKeyboard->keyboard));
wlr_seat_keyboard_notify_modifiers(g_pCompositor->m_sSeat.seat, &MODS);
@@ -1273,8 +1277,7 @@ void CInputManager::onKeyboardMod(void* data, SKeyboard* pKeyboard) {
bool CInputManager::shouldIgnoreVirtualKeyboard(SKeyboard* pKeyboard) {
return !pKeyboard ||
- (m_sIMERelay.m_pKeyboardGrab &&
- wl_resource_get_client(m_sIMERelay.m_pKeyboardGrab->pWlrKbGrab->resource) == wl_resource_get_client(wlr_input_device_get_virtual_keyboard(pKeyboard->keyboard)->resource));
+ (!m_sIMERelay.m_pIME.expired() && m_sIMERelay.m_pIME.lock()->grabClient() == wl_resource_get_client(wlr_input_device_get_virtual_keyboard(pKeyboard->keyboard)->resource));
}
void CInputManager::refocus() {
diff --git a/src/managers/input/InputMethodPopup.cpp b/src/managers/input/InputMethodPopup.cpp
index 3a56d30c..f2d49ae8 100644
--- a/src/managers/input/InputMethodPopup.cpp
+++ b/src/managers/input/InputMethodPopup.cpp
@@ -2,37 +2,14 @@
#include "InputManager.hpp"
#include "../../Compositor.hpp"
#include "../../protocols/FractionalScale.hpp"
+#include "../../protocols/InputMethodV2.hpp"
-CInputPopup::CInputPopup(wlr_input_popup_surface_v2* surf) : pWlr(surf) {
- surface.assign(surf->surface);
- initCallbacks();
-}
-
-static void onCommit(void* owner, void* data) {
- const auto PPOPUP = (CInputPopup*)owner;
- PPOPUP->onCommit();
-}
-
-static void onMap(void* owner, void* data) {
- const auto PPOPUP = (CInputPopup*)owner;
- PPOPUP->onMap();
-}
-
-static void onUnmap(void* owner, void* data) {
- const auto PPOPUP = (CInputPopup*)owner;
- PPOPUP->onUnmap();
-}
-
-static void onDestroy(void* owner, void* data) {
- const auto PPOPUP = (CInputPopup*)owner;
- PPOPUP->onDestroy();
-}
-
-void CInputPopup::initCallbacks() {
- hyprListener_commitPopup.initCallback(&pWlr->surface->events.commit, &::onCommit, this, "IME Popup");
- hyprListener_mapPopup.initCallback(&pWlr->surface->events.map, &::onMap, this, "IME Popup");
- hyprListener_unmapPopup.initCallback(&pWlr->surface->events.unmap, &::onUnmap, this, "IME Popup");
- hyprListener_destroyPopup.initCallback(&pWlr->events.destroy, &::onDestroy, this, "IME Popup");
+CInputPopup::CInputPopup(SP popup_) : popup(popup_) {
+ listeners.commit = popup_->events.commit.registerListener([this](std::any d) { onCommit(); });
+ listeners.map = popup_->events.map.registerListener([this](std::any d) { onMap(); });
+ listeners.unmap = popup_->events.unmap.registerListener([this](std::any d) { onUnmap(); });
+ listeners.destroy = popup_->events.destroy.registerListener([this](std::any d) { onDestroy(); });
+ surface.assign(popup_->surface());
}
CWLSurface* CInputPopup::queryOwner() {
@@ -45,11 +22,6 @@ CWLSurface* CInputPopup::queryOwner() {
}
void CInputPopup::onDestroy() {
- hyprListener_commitPopup.removeCallback();
- hyprListener_destroyPopup.removeCallback();
- hyprListener_mapPopup.removeCallback();
- hyprListener_unmapPopup.removeCallback();
-
g_pInputManager->m_sIMERelay.removePopup(this);
}
@@ -101,7 +73,7 @@ void CInputPopup::damageSurface() {
}
void CInputPopup::updateBox() {
- if (!surface.wlr()->mapped)
+ if (!popup.lock()->mapped)
return;
const auto OWNER = queryOwner();
@@ -142,7 +114,7 @@ void CInputPopup::updateBox() {
popupOffset.x -= popupOverflow;
CBox cursorBoxLocal({-popupOffset.x, -popupOffset.y}, cursorBoxParent.size());
- wlr_input_popup_surface_v2_send_text_input_rectangle(pWlr, cursorBoxLocal.pWlr());
+ popup.lock()->sendInputRectangle(cursorBoxLocal);
CBox popupBoxParent(cursorBoxParent.pos() + popupOffset, currentPopupSize);
if (popupBoxParent != lastBoxLocal) {
diff --git a/src/managers/input/InputMethodPopup.hpp b/src/managers/input/InputMethodPopup.hpp
index aed4dbc2..61b45d74 100644
--- a/src/managers/input/InputMethodPopup.hpp
+++ b/src/managers/input/InputMethodPopup.hpp
@@ -4,17 +4,13 @@
#include "../../desktop/WLSurface.hpp"
#include "../../macros.hpp"
#include "../../helpers/Box.hpp"
+#include "../../helpers/signal/Listener.hpp"
-struct wlr_input_popup_surface_v2;
+class CInputMethodPopupV2;
class CInputPopup {
public:
- CInputPopup(wlr_input_popup_surface_v2* surf);
-
- void onDestroy();
- void onMap();
- void onUnmap();
- void onCommit();
+ CInputPopup(SP popup);
void damageEntire();
void damageSurface();
@@ -24,18 +20,25 @@ class CInputPopup {
CBox globalBox();
wlr_surface* getWlrSurface();
+ void onCommit();
+
private:
- void initCallbacks();
- CWLSurface* queryOwner();
- void updateBox();
+ CWLSurface* queryOwner();
+ void updateBox();
- wlr_input_popup_surface_v2* pWlr = nullptr;
- CWLSurface surface;
- CBox lastBoxLocal;
- uint64_t lastMonitor = -1;
+ void onDestroy();
+ void onMap();
+ void onUnmap();
- DYNLISTENER(mapPopup);
- DYNLISTENER(unmapPopup);
- DYNLISTENER(destroyPopup);
- DYNLISTENER(commitPopup);
+ WP popup;
+ CWLSurface surface;
+ CBox lastBoxLocal;
+ uint64_t lastMonitor = -1;
+
+ struct {
+ CHyprSignalListener map;
+ CHyprSignalListener unmap;
+ CHyprSignalListener destroy;
+ CHyprSignalListener commit;
+ } listeners;
};
diff --git a/src/managers/input/InputMethodRelay.cpp b/src/managers/input/InputMethodRelay.cpp
index 4f1cd278..778f3086 100644
--- a/src/managers/input/InputMethodRelay.cpp
+++ b/src/managers/input/InputMethodRelay.cpp
@@ -2,98 +2,53 @@
#include "InputManager.hpp"
#include "../../Compositor.hpp"
#include "../../protocols/TextInputV3.hpp"
+#include "../../protocols/InputMethodV2.hpp"
CInputMethodRelay::CInputMethodRelay() {
static auto P = g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast(param)); });
listeners.newTIV3 = PROTO::textInputV3->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(ti); });
+ listeners.newIME = PROTO::ime->events.newIME.registerListener([this](std::any ime) { onNewIME(std::any_cast>(ime)); });
}
-void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) {
- if (m_pWLRIME) {
+void CInputMethodRelay::onNewIME(SP pIME) {
+ if (!m_pIME.expired()) {
Debug::log(ERR, "Cannot register 2 IMEs at once!");
- wlr_input_method_v2_send_unavailable(pIME);
+ pIME->unavailable();
return;
}
- m_pWLRIME = pIME;
+ m_pIME = pIME;
- hyprListener_IMECommit.initCallback(
- &m_pWLRIME->events.commit,
- [&](void* owner, void* data) {
- const auto PTI = getFocusedTextInput();
- const auto PIMR = (CInputMethodRelay*)owner;
+ listeners.commitIME = pIME->events.onCommit.registerListener([this](std::any d) {
+ const auto PTI = getFocusedTextInput();
- if (!PTI) {
- Debug::log(LOG, "No focused TextInput on IME Commit");
- return;
- }
+ if (!PTI) {
+ Debug::log(LOG, "No focused TextInput on IME Commit");
+ return;
+ }
- PTI->updateIMEState(PIMR->m_pWLRIME);
- },
- this, "IMERelay");
+ PTI->updateIMEState(m_pIME.lock());
+ });
- hyprListener_IMEDestroy.initCallback(
- &m_pWLRIME->events.destroy,
- [&](void* owner, void* data) {
- m_pWLRIME = nullptr;
+ listeners.destroyIME = pIME->events.destroy.registerListener([this](std::any d) {
+ const auto PTI = getFocusedTextInput();
- hyprListener_IMEDestroy.removeCallback();
- hyprListener_IMECommit.removeCallback();
- hyprListener_IMEGrab.removeCallback();
- hyprListener_IMENewPopup.removeCallback();
+ Debug::log(LOG, "IME Destroy");
- m_pKeyboardGrab.reset(nullptr);
+ if (PTI)
+ PTI->leave();
- const auto PTI = getFocusedTextInput();
+ m_pIME.reset();
+ });
- Debug::log(LOG, "IME Destroy");
+ listeners.newPopup = pIME->events.newPopup.registerListener([this](std::any d) {
+ m_vIMEPopups.emplace_back(std::make_unique(std::any_cast>(d)));
- if (PTI)
- PTI->leave();
- },
- this, "IMERelay");
-
- hyprListener_IMEGrab.initCallback(
- &m_pWLRIME->events.grab_keyboard,
- [&](void* owner, void* data) {
- Debug::log(LOG, "IME TextInput Keyboard Grab new");
-
- m_pKeyboardGrab.reset(nullptr);
-
- m_pKeyboardGrab = std::make_unique();
-
- m_pKeyboardGrab->pKeyboard = wlr_seat_get_keyboard(g_pCompositor->m_sSeat.seat);
-
- const auto PKBGRAB = (wlr_input_method_keyboard_grab_v2*)data;
-
- m_pKeyboardGrab->pWlrKbGrab = PKBGRAB;
-
- wlr_input_method_keyboard_grab_v2_set_keyboard(m_pKeyboardGrab->pWlrKbGrab, m_pKeyboardGrab->pKeyboard);
-
- m_pKeyboardGrab->hyprListener_grabDestroy.initCallback(
- &PKBGRAB->events.destroy,
- [&](void* owner, void* data) {
- m_pKeyboardGrab->hyprListener_grabDestroy.removeCallback();
-
- Debug::log(LOG, "IME TextInput Keyboard Grab destroy");
-
- m_pKeyboardGrab.reset(nullptr);
- },
- m_pKeyboardGrab.get(), "IME Keyboard Grab");
- },
- this, "IMERelay");
-
- hyprListener_IMENewPopup.initCallback(
- &m_pWLRIME->events.new_popup_surface,
- [&](void* owner, void* data) {
- m_vIMEPopups.emplace_back(std::make_unique((wlr_input_popup_surface_v2*)data));
-
- Debug::log(LOG, "New input popup");
- },
- this, "IMERelay");
+ Debug::log(LOG, "New input popup");
+ });
if (!g_pCompositor->m_pLastFocus)
return;
@@ -117,22 +72,6 @@ void CInputMethodRelay::removePopup(CInputPopup* pPopup) {
std::erase_if(m_vIMEPopups, [pPopup](const auto& other) { return other.get() == pPopup; });
}
-SIMEKbGrab* CInputMethodRelay::getIMEKeyboardGrab(SKeyboard* pKeyboard) {
-
- if (!m_pWLRIME)
- return nullptr;
-
- if (!m_pKeyboardGrab.get())
- return nullptr;
-
- const auto VIRTKB = wlr_input_device_get_virtual_keyboard(pKeyboard->keyboard);
-
- if (VIRTKB && (wl_resource_get_client(VIRTKB->resource) == wl_resource_get_client(m_pKeyboardGrab->pWlrKbGrab->resource)))
- return nullptr;
-
- return m_pKeyboardGrab.get();
-}
-
CTextInput* CInputMethodRelay::getFocusedTextInput() {
if (!g_pCompositor->m_pLastFocus)
return nullptr;
@@ -164,33 +103,30 @@ void CInputMethodRelay::updateAllPopups() {
}
void CInputMethodRelay::activateIME(CTextInput* pInput) {
- if (!m_pWLRIME)
+ if (m_pIME.expired())
return;
- wlr_input_method_v2_send_activate(g_pInputManager->m_sIMERelay.m_pWLRIME);
+ m_pIME.lock()->activate();
commitIMEState(pInput);
}
void CInputMethodRelay::deactivateIME(CTextInput* pInput) {
- if (!m_pWLRIME)
+ if (m_pIME.expired())
return;
- if (!m_pWLRIME->active)
- return;
-
- wlr_input_method_v2_send_deactivate(g_pInputManager->m_sIMERelay.m_pWLRIME);
+ m_pIME.lock()->deactivate();
commitIMEState(pInput);
}
void CInputMethodRelay::commitIMEState(CTextInput* pInput) {
- if (!m_pWLRIME)
+ if (m_pIME.expired())
return;
- pInput->commitStateToIME(m_pWLRIME);
+ pInput->commitStateToIME(m_pIME.lock());
}
void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) {
- if (!m_pWLRIME)
+ if (m_pIME.expired())
return;
if (pSurface == m_pLastKbFocus)
diff --git a/src/managers/input/InputMethodRelay.hpp b/src/managers/input/InputMethodRelay.hpp
index 1b2cb07a..2896e8e8 100644
--- a/src/managers/input/InputMethodRelay.hpp
+++ b/src/managers/input/InputMethodRelay.hpp
@@ -11,39 +11,36 @@
class CInputManager;
class CHyprRenderer;
struct STextInputV1;
+class CInputMethodV2;
class CInputMethodRelay {
public:
CInputMethodRelay();
- void onNewIME(wlr_input_method_v2*);
- void onNewTextInput(std::any tiv3);
- void onNewTextInput(STextInputV1* pTIV1);
+ void onNewIME(SP);
+ void onNewTextInput(std::any tiv3);
+ void onNewTextInput(STextInputV1* pTIV1);
- wlr_input_method_v2* m_pWLRIME = nullptr;
+ void activateIME(CTextInput* pInput);
+ void deactivateIME(CTextInput* pInput);
+ void commitIMEState(CTextInput* pInput);
+ void removeTextInput(CTextInput* pInput);
- void activateIME(CTextInput* pInput);
- void deactivateIME(CTextInput* pInput);
- void commitIMEState(CTextInput* pInput);
- void removeTextInput(CTextInput* pInput);
+ void onKeyboardFocus(wlr_surface*);
- void onKeyboardFocus(wlr_surface*);
+ CTextInput* getFocusedTextInput();
- CTextInput* getFocusedTextInput();
+ void setIMEPopupFocus(CInputPopup*, wlr_surface*);
+ void removePopup(CInputPopup*);
- SIMEKbGrab* getIMEKeyboardGrab(SKeyboard*);
+ CInputPopup* popupFromCoords(const Vector2D& point);
+ CInputPopup* popupFromSurface(const wlr_surface* surface);
- void setIMEPopupFocus(CInputPopup*, wlr_surface*);
- void removePopup(CInputPopup*);
+ void updateAllPopups();
- CInputPopup* popupFromCoords(const Vector2D& point);
- CInputPopup* popupFromSurface(const wlr_surface* surface);
-
- void updateAllPopups();
+ WP m_pIME;
private:
- std::unique_ptr m_pKeyboardGrab;
-
std::vector> m_vTextInputs;
std::vector> m_vIMEPopups;
@@ -51,14 +48,12 @@ class CInputMethodRelay {
struct {
CHyprSignalListener newTIV3;
+ CHyprSignalListener newIME;
+ CHyprSignalListener commitIME;
+ CHyprSignalListener destroyIME;
+ CHyprSignalListener newPopup;
} listeners;
- DYNLISTENER(textInputNew);
- DYNLISTENER(IMECommit);
- DYNLISTENER(IMEDestroy);
- DYNLISTENER(IMEGrab);
- DYNLISTENER(IMENewPopup);
-
friend class CHyprRenderer;
friend class CInputManager;
friend class CTextInputV1ProtocolManager;
diff --git a/src/managers/input/TextInput.cpp b/src/managers/input/TextInput.cpp
index 12f13971..77718174 100644
--- a/src/managers/input/TextInput.cpp
+++ b/src/managers/input/TextInput.cpp
@@ -4,6 +4,7 @@
#include "../../protocols/TextInputV1.hpp"
#include "../../Compositor.hpp"
#include "../../protocols/TextInputV3.hpp"
+#include "../../protocols/InputMethodV2.hpp"
CTextInput::CTextInput(STextInputV1* ti) : pV1Input(ti) {
ti->pTextInput = this;
@@ -67,7 +68,7 @@ void CTextInput::initCallbacks() {
void CTextInput::onEnabled(wlr_surface* surfV1) {
Debug::log(LOG, "TI ENABLE");
- if (!g_pInputManager->m_sIMERelay.m_pWLRIME) {
+ if (g_pInputManager->m_sIMERelay.m_pIME.expired()) {
// Debug::log(WARN, "Enabling TextInput on no IME!");
return;
}
@@ -85,7 +86,7 @@ void CTextInput::onEnabled(wlr_surface* surfV1) {
}
void CTextInput::onDisabled() {
- if (!g_pInputManager->m_sIMERelay.m_pWLRIME) {
+ if (g_pInputManager->m_sIMERelay.m_pIME.expired()) {
// Debug::log(WARN, "Disabling TextInput on no IME!");
return;
}
@@ -103,7 +104,7 @@ void CTextInput::onDisabled() {
}
void CTextInput::onCommit() {
- if (!g_pInputManager->m_sIMERelay.m_pWLRIME) {
+ if (g_pInputManager->m_sIMERelay.m_pIME.expired()) {
// Debug::log(WARN, "Committing TextInput on no IME!");
return;
}
@@ -218,67 +219,66 @@ wl_client* CTextInput::client() {
return isV3() ? pV3Input.lock()->client() : pV1Input->client;
}
-void CTextInput::commitStateToIME(wlr_input_method_v2* ime) {
+void CTextInput::commitStateToIME(SP ime) {
if (isV3()) {
const auto INPUT = pV3Input.lock();
if (INPUT->current.surrounding.updated)
- wlr_input_method_v2_send_surrounding_text(ime, INPUT->current.surrounding.text.c_str(), INPUT->current.surrounding.cursor, INPUT->current.surrounding.anchor);
+ ime->surroundingText(INPUT->current.surrounding.text, INPUT->current.surrounding.cursor, INPUT->current.surrounding.anchor);
- wlr_input_method_v2_send_text_change_cause(ime, INPUT->current.cause);
+ ime->textChangeCause(INPUT->current.cause);
if (INPUT->current.contentType.updated)
- wlr_input_method_v2_send_content_type(ime, INPUT->current.contentType.hint, INPUT->current.contentType.purpose);
+ ime->textContentType(INPUT->current.contentType.hint, INPUT->current.contentType.purpose);
} else {
if (pV1Input->pendingSurrounding.isPending)
- wlr_input_method_v2_send_surrounding_text(ime, pV1Input->pendingSurrounding.text.c_str(), pV1Input->pendingSurrounding.cursor, pV1Input->pendingSurrounding.anchor);
+ ime->surroundingText(pV1Input->pendingSurrounding.text, pV1Input->pendingSurrounding.cursor, pV1Input->pendingSurrounding.anchor);
- wlr_input_method_v2_send_text_change_cause(ime, 0);
+ ime->textChangeCause(ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD);
if (pV1Input->pendingContentType.isPending)
- wlr_input_method_v2_send_content_type(ime, pV1Input->pendingContentType.hint, pV1Input->pendingContentType.purpose);
+ ime->textContentType((zwpTextInputV3ContentHint)pV1Input->pendingContentType.hint, (zwpTextInputV3ContentPurpose)pV1Input->pendingContentType.purpose);
}
g_pInputManager->m_sIMERelay.updateAllPopups();
- wlr_input_method_v2_send_done(ime);
+ ime->done();
}
-void CTextInput::updateIMEState(wlr_input_method_v2* ime) {
+void CTextInput::updateIMEState(SP ime) {
if (isV3()) {
const auto INPUT = pV3Input.lock();
- if (ime->current.preedit.text)
- INPUT->preeditString(ime->current.preedit.text, ime->current.preedit.cursor_begin, ime->current.preedit.cursor_end);
+ if (ime->current.preeditString.committed)
+ INPUT->preeditString(ime->current.preeditString.string, ime->current.preeditString.begin, ime->current.preeditString.end);
- if (ime->current.commit_text)
- INPUT->commitString(ime->current.commit_text);
+ if (ime->current.committedString.committed)
+ INPUT->commitString(ime->current.committedString.string);
- if (ime->current.delete_.before_length || ime->current.delete_.after_length)
- INPUT->deleteSurroundingText(ime->current.delete_.before_length, ime->current.delete_.after_length);
+ if (ime->current.deleteSurrounding.committed)
+ INPUT->deleteSurroundingText(ime->current.deleteSurrounding.before, ime->current.deleteSurrounding.after);
INPUT->sendDone();
} else {
- if (ime->current.preedit.text) {
- zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preedit.cursor_begin);
- zwp_text_input_v1_send_preedit_styling(pV1Input->resourceImpl, 0, std::string(ime->current.preedit.text).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
- zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preedit.text, "");
+ if (ime->current.preeditString.committed) {
+ zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preeditString.begin);
+ zwp_text_input_v1_send_preedit_styling(pV1Input->resourceImpl, 0, std::string(ime->current.preeditString.string).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
+ zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preeditString.string.c_str(), "");
} else {
- zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preedit.cursor_begin);
+ zwp_text_input_v1_send_preedit_cursor(pV1Input->resourceImpl, ime->current.preeditString.begin);
zwp_text_input_v1_send_preedit_styling(pV1Input->resourceImpl, 0, 0, ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, "", "");
}
- if (ime->current.commit_text) {
- zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.commit_text);
- }
+ if (ime->current.committedString.committed)
+ zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.committedString.string.c_str());
- if (ime->current.delete_.before_length || ime->current.delete_.after_length) {
- zwp_text_input_v1_send_delete_surrounding_text(pV1Input->resourceImpl, std::string(ime->current.preedit.text).length() - ime->current.delete_.before_length,
- ime->current.delete_.after_length + ime->current.delete_.before_length);
+ if (ime->current.deleteSurrounding.committed) {
+ zwp_text_input_v1_send_delete_surrounding_text(pV1Input->resourceImpl, std::string(ime->current.preeditString.string).length() - ime->current.deleteSurrounding.before,
+ ime->current.deleteSurrounding.after + ime->current.deleteSurrounding.before);
- if (ime->current.preedit.text)
- zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preedit.text);
+ if (ime->current.preeditString.committed)
+ zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preeditString.string.c_str());
}
}
}
diff --git a/src/managers/input/TextInput.hpp b/src/managers/input/TextInput.hpp
index 6d7e9c59..481d60c1 100644
--- a/src/managers/input/TextInput.hpp
+++ b/src/managers/input/TextInput.hpp
@@ -11,6 +11,7 @@ struct wl_client;
struct STextInputV1;
class CTextInputV3;
+class CInputMethodV2;
class CTextInput {
public:
@@ -23,8 +24,8 @@ class CTextInput {
void leave();
void tiV1Destroyed();
wl_client* client();
- void commitStateToIME(wlr_input_method_v2* ime);
- void updateIMEState(wlr_input_method_v2* ime);
+ void commitStateToIME(SP ime);
+ void updateIMEState(SP ime);
void onEnabled(wlr_surface* surfV1 = nullptr);
void onDisabled();
diff --git a/src/protocols/InputMethodV2.cpp b/src/protocols/InputMethodV2.cpp
new file mode 100644
index 00000000..91dac671
--- /dev/null
+++ b/src/protocols/InputMethodV2.cpp
@@ -0,0 +1,383 @@
+#include "InputMethodV2.hpp"
+#include "../Compositor.hpp"
+#include
+
+#define LOGM PROTO::ime->protoLog
+
+CInputMethodKeyboardGrabV2::CInputMethodKeyboardGrabV2(SP resource_, SP owner_) : resource(resource_), owner(owner_) {
+ if (!resource->resource())
+ return;
+
+ resource->setRelease([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); });
+ resource->setOnDestroy([this](CZwpInputMethodKeyboardGrabV2* r) { PROTO::ime->destroyResource(this); });
+
+ const auto PKEYBOARD = wlr_seat_get_keyboard(g_pCompositor->m_sSeat.seat);
+
+ if (!PKEYBOARD) {
+ LOGM(ERR, "IME called but no active keyboard???");
+ return;
+ }
+
+ sendKeyboardData(PKEYBOARD);
+}
+
+CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() {
+ if (!owner.expired())
+ std::erase_if(owner.lock()->grabs, [](const auto& g) { return g.expired(); });
+}
+
+void CInputMethodKeyboardGrabV2::sendKeyboardData(wlr_keyboard* keyboard) {
+
+ if (keyboard == pLastKeyboard)
+ return;
+
+ pLastKeyboard = keyboard;
+
+ int keymapFD = allocateSHMFile(keyboard->keymap_size);
+ if (keymapFD < 0) {
+ LOGM(ERR, "Failed to create a keymap file for keyboard grab");
+ return;
+ }
+
+ void* data = mmap(nullptr, keyboard->keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, keymapFD, 0);
+ if (data == MAP_FAILED) {
+ LOGM(ERR, "Failed to mmap a keymap file for keyboard grab");
+ close(keymapFD);
+ return;
+ }
+
+ memcpy(data, keyboard->keymap_string, keyboard->keymap_size);
+ munmap(data, keyboard->keymap_size);
+
+ resource->sendKeymap(WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymapFD, keyboard->keymap_size);
+
+ close(keymapFD);
+
+ sendMods(0, 0, 0, 0);
+
+ resource->sendRepeatInfo(keyboard->repeat_info.rate, keyboard->repeat_info.delay);
+}
+
+void CInputMethodKeyboardGrabV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state) {
+ const auto SERIAL = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, wl_resource_get_client(resource->resource())));
+
+ resource->sendKey(SERIAL, time, key, (uint32_t)state);
+}
+
+void CInputMethodKeyboardGrabV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
+ const auto SERIAL = wlr_seat_client_next_serial(wlr_seat_client_for_wl_client(g_pCompositor->m_sSeat.seat, wl_resource_get_client(resource->resource())));
+
+ resource->sendModifiers(SERIAL, depressed, latched, locked, group);
+}
+
+bool CInputMethodKeyboardGrabV2::good() {
+ return resource->resource();
+}
+
+SP CInputMethodKeyboardGrabV2::getOwner() {
+ return owner.lock();
+}
+
+wl_client* CInputMethodKeyboardGrabV2::client() {
+ return wl_resource_get_client(resource->resource());
+}
+
+CInputMethodPopupV2::CInputMethodPopupV2(SP resource_, SP owner_, wlr_surface* wlrSurface) : resource(resource_), owner(owner_) {
+ if (!resource->resource())
+ return;
+
+ resource->setDestroy([this](CZwpInputPopupSurfaceV2* r) { PROTO::ime->destroyResource(this); });
+ resource->setOnDestroy([this](CZwpInputPopupSurfaceV2* r) { PROTO::ime->destroyResource(this); });
+
+ pSurface = wlrSurface;
+
+ hyprListener_destroySurface.initCallback(
+ &wlrSurface->events.destroy,
+ [this](void* owner, void* data) {
+ if (mapped)
+ events.unmap.emit();
+
+ hyprListener_commitSurface.removeCallback();
+ hyprListener_destroySurface.removeCallback();
+
+ if (g_pCompositor->m_pLastFocus == pSurface)
+ g_pCompositor->m_pLastFocus = nullptr;
+
+ pSurface = nullptr;
+ },
+ this, "IMEPopup");
+
+ hyprListener_commitSurface.initCallback(
+ &wlrSurface->events.commit,
+ [this](void* owner, void* data) {
+ if (pSurface->pending.buffer_width > 0 && pSurface->pending.buffer_height > 0 && !mapped) {
+ mapped = true;
+ wlr_surface_map(pSurface);
+ events.map.emit();
+ return;
+ }
+
+ if (pSurface->pending.buffer_width <= 0 && pSurface->pending.buffer_height <= 0 && mapped) {
+ mapped = false;
+ wlr_surface_unmap(pSurface);
+ events.unmap.emit();
+ return;
+ }
+
+ events.commit.emit();
+ },
+ this, "IMEPopup");
+}
+
+CInputMethodPopupV2::~CInputMethodPopupV2() {
+ if (!owner.expired())
+ std::erase_if(owner.lock()->popups, [](const auto& p) { return p.expired(); });
+
+ events.destroy.emit();
+}
+
+bool CInputMethodPopupV2::good() {
+ return resource->resource();
+}
+
+void CInputMethodPopupV2::sendInputRectangle(const CBox& box) {
+ resource->sendTextInputRectangle(box.x, box.y, box.w, box.h);
+}
+
+wlr_surface* CInputMethodPopupV2::surface() {
+ return pSurface;
+}
+
+void CInputMethodV2::SState::reset() {
+ committedString.committed = false;
+ deleteSurrounding.committed = false;
+ preeditString.committed = false;
+}
+
+CInputMethodV2::CInputMethodV2(SP resource_) : resource(resource_) {
+ if (!resource->resource())
+ return;
+
+ resource->setDestroy([this](CZwpInputMethodV2* r) {
+ events.destroy.emit();
+ PROTO::ime->destroyResource(this);
+ });
+ resource->setOnDestroy([this](CZwpInputMethodV2* r) {
+ events.destroy.emit();
+ PROTO::ime->destroyResource(this);
+ });
+
+ resource->setCommitString([this](CZwpInputMethodV2* r, const char* str) {
+ pending.committedString.string = str;
+ pending.committedString.committed = true;
+ });
+
+ resource->setDeleteSurroundingText([this](CZwpInputMethodV2* r, uint32_t before, uint32_t after) {
+ pending.deleteSurrounding.before = before;
+ pending.deleteSurrounding.after = after;
+ pending.deleteSurrounding.committed = true;
+ });
+
+ resource->setSetPreeditString([this](CZwpInputMethodV2* r, const char* str, int32_t begin, int32_t end) {
+ pending.preeditString.string = str;
+ pending.preeditString.begin = begin;
+ pending.preeditString.end = end;
+ pending.preeditString.committed = true;
+ });
+
+ resource->setCommit([this](CZwpInputMethodV2* r, uint32_t serial) {
+ current = pending;
+ pending.reset();
+ events.onCommit.emit();
+ });
+
+ resource->setGetInputPopupSurface([this](CZwpInputMethodV2* r, uint32_t id, wl_resource* surface) {
+ const auto CLIENT = wl_resource_get_client(r->resource());
+ const auto RESOURCE = PROTO::ime->m_vPopups.emplace_back(std::make_shared(
+ std::make_shared(CLIENT, wl_resource_get_version(r->resource()), id), self.lock(), wlr_surface_from_resource(surface)));
+
+ if (!RESOURCE->good()) {
+ wl_resource_post_no_memory(r->resource());
+ PROTO::ime->m_vPopups.pop_back();
+ return;
+ }
+
+ LOGM(LOG, "New IME Popup with resource id {}", id);
+
+ popups.emplace_back(RESOURCE);
+
+ events.newPopup.emit(RESOURCE);
+ });
+
+ resource->setGrabKeyboard([this](CZwpInputMethodV2* r, uint32_t id) {
+ const auto CLIENT = wl_resource_get_client(r->resource());
+ const auto RESOURCE = PROTO::ime->m_vGrabs.emplace_back(
+ std::make_shared(std::make_shared(CLIENT, wl_resource_get_version(r->resource()), id), self.lock()));
+
+ if (!RESOURCE->good()) {
+ wl_resource_post_no_memory(r->resource());
+ PROTO::ime->m_vGrabs.pop_back();
+ return;
+ }
+
+ LOGM(LOG, "New IME Grab with resource id {}", id);
+
+ grabs.emplace_back(RESOURCE);
+ });
+}
+
+CInputMethodV2::~CInputMethodV2() {
+ events.destroy.emit();
+}
+
+bool CInputMethodV2::good() {
+ return resource->resource();
+}
+
+void CInputMethodV2::activate() {
+ if (active)
+ return;
+ resource->sendActivate();
+ active = true;
+}
+
+void CInputMethodV2::deactivate() {
+ if (!active)
+ return;
+ resource->sendDeactivate();
+ active = false;
+}
+
+void CInputMethodV2::surroundingText(const std::string& text, uint32_t cursor, uint32_t anchor) {
+ resource->sendSurroundingText(text.c_str(), cursor, anchor);
+}
+
+void CInputMethodV2::textChangeCause(zwpTextInputV3ChangeCause changeCause) {
+ resource->sendTextChangeCause((uint32_t)changeCause);
+}
+
+void CInputMethodV2::textContentType(zwpTextInputV3ContentHint hint, zwpTextInputV3ContentPurpose purpose) {
+ resource->sendContentType((uint32_t)hint, (uint32_t)purpose);
+}
+
+void CInputMethodV2::done() {
+ resource->sendDone();
+}
+
+void CInputMethodV2::unavailable() {
+ resource->sendUnavailable();
+}
+
+bool CInputMethodV2::hasGrab() {
+ return !grabs.empty();
+}
+
+wl_client* CInputMethodV2::grabClient() {
+ if (grabs.empty())
+ return nullptr;
+
+ for (auto& gw : grabs) {
+ auto g = gw.lock();
+
+ if (!g)
+ continue;
+
+ return g->client();
+ }
+
+ return nullptr;
+}
+
+void CInputMethodV2::sendInputRectangle(const CBox& box) {
+ inputRectangle = box;
+ for (auto& wp : popups) {
+ auto p = wp.lock();
+
+ if (!p)
+ continue;
+
+ p->sendInputRectangle(inputRectangle);
+ }
+}
+
+void CInputMethodV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state) {
+ for (auto& gw : grabs) {
+ auto g = gw.lock();
+
+ if (!g)
+ continue;
+
+ g->sendKey(time, key, state);
+ }
+}
+
+void CInputMethodV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
+ for (auto& gw : grabs) {
+ auto g = gw.lock();
+
+ if (!g)
+ continue;
+
+ g->sendMods(depressed, latched, locked, group);
+ }
+}
+
+void CInputMethodV2::setKeyboard(wlr_keyboard* keyboard) {
+ for (auto& gw : grabs) {
+ auto g = gw.lock();
+
+ if (!g)
+ continue;
+
+ g->sendKeyboardData(keyboard);
+ }
+}
+
+wl_client* CInputMethodV2::client() {
+ return wl_resource_get_client(resource->resource());
+}
+
+CInputMethodV2Protocol::CInputMethodV2Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
+ ;
+}
+
+void CInputMethodV2Protocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
+ const auto RESOURCE = m_vManagers.emplace_back(std::make_unique(client, ver, id)).get();
+ RESOURCE->setOnDestroy([this](CZwpInputMethodManagerV2* p) { this->onManagerResourceDestroy(p->resource()); });
+
+ RESOURCE->setDestroy([this](CZwpInputMethodManagerV2* pMgr) { this->onManagerResourceDestroy(pMgr->resource()); });
+ RESOURCE->setGetInputMethod([this](CZwpInputMethodManagerV2* pMgr, wl_resource* seat, uint32_t id) { this->onGetIME(pMgr, seat, id); });
+}
+
+void CInputMethodV2Protocol::onManagerResourceDestroy(wl_resource* res) {
+ std::erase_if(m_vManagers, [&](const auto& other) { return other->resource() == res; });
+}
+
+void CInputMethodV2Protocol::destroyResource(CInputMethodPopupV2* popup) {
+ std::erase_if(m_vPopups, [&](const auto& other) { return other.get() == popup; });
+}
+
+void CInputMethodV2Protocol::destroyResource(CInputMethodKeyboardGrabV2* grab) {
+ std::erase_if(m_vGrabs, [&](const auto& other) { return other.get() == grab; });
+}
+
+void CInputMethodV2Protocol::destroyResource(CInputMethodV2* ime) {
+ std::erase_if(m_vIMEs, [&](const auto& other) { return other.get() == ime; });
+}
+
+void CInputMethodV2Protocol::onGetIME(CZwpInputMethodManagerV2* mgr, wl_resource* seat, uint32_t id) {
+ const auto CLIENT = wl_resource_get_client(mgr->resource());
+ const auto RESOURCE = m_vIMEs.emplace_back(std::make_shared(std::make_shared(CLIENT, wl_resource_get_version(mgr->resource()), id)));
+
+ if (!RESOURCE->good()) {
+ wl_resource_post_no_memory(mgr->resource());
+ m_vIMEs.pop_back();
+ return;
+ }
+
+ RESOURCE->self = RESOURCE;
+
+ LOGM(LOG, "New IME with resource id {}", id);
+
+ events.newIME.emit(RESOURCE);
+}
\ No newline at end of file
diff --git a/src/protocols/InputMethodV2.hpp b/src/protocols/InputMethodV2.hpp
new file mode 100644
index 00000000..1f5d2598
--- /dev/null
+++ b/src/protocols/InputMethodV2.hpp
@@ -0,0 +1,160 @@
+#pragma once
+
+#include
+#include
+#include
+#include "WaylandProtocol.hpp"
+#include "input-method-unstable-v2.hpp"
+#include "text-input-unstable-v3.hpp"
+#include "../helpers/signal/Signal.hpp"
+#include "../desktop/WLSurface.hpp"
+
+class CInputMethodKeyboardGrabV2;
+class CInputMethodPopupV2;
+
+class CInputMethodV2 {
+ public:
+ CInputMethodV2(SP resource_);
+ ~CInputMethodV2();
+
+ struct {
+ CSignal onCommit;
+ CSignal destroy;
+ CSignal newPopup;
+ } events;
+
+ struct SState {
+ void reset();
+
+ struct {
+ std::string string;
+ bool committed = false;
+ } committedString;
+
+ struct {
+ std::string string;
+ int32_t begin = 0, end = 0;
+ bool committed = false;
+ } preeditString;
+
+ struct {
+ uint32_t before = 0, after = 0;
+ bool committed = false;
+ } deleteSurrounding;
+ };
+
+ SState pending, current;
+
+ bool good();
+ void activate();
+ void deactivate();
+ void surroundingText(const std::string& text, uint32_t cursor, uint32_t anchor);
+ void textChangeCause(zwpTextInputV3ChangeCause changeCause);
+ void textContentType(zwpTextInputV3ContentHint hint, zwpTextInputV3ContentPurpose purpose);
+ void done();
+ void unavailable();
+
+ void sendInputRectangle(const CBox& box);
+ bool hasGrab();
+ void sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state);
+ void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
+ void setKeyboard(wlr_keyboard* keyboard);
+
+ wl_client* client();
+ wl_client* grabClient();
+
+ private:
+ SP resource;
+ std::vector> grabs;
+ std::vector> popups;
+
+ WP self;
+
+ bool active = false;
+
+ CBox inputRectangle;
+
+ friend class CInputMethodPopupV2;
+ friend class CInputMethodKeyboardGrabV2;
+ friend class CInputMethodV2Protocol;
+};
+
+class CInputMethodKeyboardGrabV2 {
+ public:
+ CInputMethodKeyboardGrabV2(SP resource_, SP owner_);
+ ~CInputMethodKeyboardGrabV2();
+
+ bool good();
+ SP getOwner();
+ wl_client* client();
+
+ void sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state);
+ void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
+ void sendKeyboardData(wlr_keyboard* keyboard);
+
+ private:
+ SP resource;
+ WP owner;
+
+ wlr_keyboard* pLastKeyboard = nullptr; // READ-ONLY
+};
+
+class CInputMethodPopupV2 {
+ public:
+ CInputMethodPopupV2(SP resource_, SP owner_, wlr_surface* surface);
+ ~CInputMethodPopupV2();
+
+ bool good();
+ void sendInputRectangle(const CBox& box);
+ wlr_surface* surface();
+
+ struct {
+ CSignal map;
+ CSignal unmap;
+ CSignal commit;
+ CSignal destroy;
+ } events;
+
+ bool mapped = false;
+
+ private:
+ SP resource;
+ WP owner;
+ wlr_surface* pSurface = nullptr;
+
+ DYNLISTENER(commitSurface);
+ DYNLISTENER(destroySurface);
+};
+
+class CInputMethodV2Protocol : public IWaylandProtocol {
+ public:
+ CInputMethodV2Protocol(const wl_interface* iface, const int& ver, const std::string& name);
+
+ virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
+
+ struct {
+ CSignal newIME; // SP
+ } events;
+
+ private:
+ void onManagerResourceDestroy(wl_resource* res);
+ void destroyResource(CInputMethodPopupV2* popup);
+ void destroyResource(CInputMethodKeyboardGrabV2* grab);
+ void destroyResource(CInputMethodV2* ime);
+
+ void onGetIME(CZwpInputMethodManagerV2* mgr, wl_resource* seat, uint32_t id);
+
+ //
+ std::vector> m_vManagers;
+ std::vector> m_vIMEs;
+ std::vector> m_vGrabs;
+ std::vector> m_vPopups;
+
+ friend class CInputMethodPopupV2;
+ friend class CInputMethodKeyboardGrabV2;
+ friend class CInputMethodV2;
+};
+
+namespace PROTO {
+ inline UP ime;
+};
\ No newline at end of file