mirror of
https://github.com/hyprwm/Hyprland
synced 2024-11-26 05:45:59 +01:00
ime-v2: move to new impl
This commit is contained in:
parent
4ed6b69b68
commit
8bcccf9f0f
22 changed files with 1208 additions and 265 deletions
|
@ -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-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-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/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/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false)
|
||||||
protocolNew("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-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)
|
protocolNew("unstable/xdg-output/xdg-output-unstable-v1.xml" "xdg-output-unstable-v1" false)
|
||||||
|
|
494
protocols/input-method-unstable-v2.xml
Normal file
494
protocols/input-method-unstable-v2.xml
Normal file
|
@ -0,0 +1,494 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<protocol name="input_method_unstable_v2">
|
||||||
|
|
||||||
|
<copyright>
|
||||||
|
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.
|
||||||
|
</copyright>
|
||||||
|
|
||||||
|
<description summary="Protocol for creating input methods">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<interface name="zwp_input_method_v2" version="1">
|
||||||
|
<description summary="input method">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<enum name="error">
|
||||||
|
<entry name="role" value="0" summary="wl_surface has another role"/>
|
||||||
|
</enum>
|
||||||
|
|
||||||
|
<event name="activate">
|
||||||
|
<description summary="input method has been requested">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="deactivate">
|
||||||
|
<description summary="deactivate event">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="surrounding_text">
|
||||||
|
<description summary="surrounding text event">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="text" type="string"/>
|
||||||
|
<arg name="cursor" type="uint"/>
|
||||||
|
<arg name="anchor" type="uint"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="text_change_cause">
|
||||||
|
<description summary="indicates the cause of surrounding text change">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="cause" type="uint" enum="zwp_text_input_v3.change_cause"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="content_type">
|
||||||
|
<description summary="content purpose and hint">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="hint" type="uint" enum="zwp_text_input_v3.content_hint"/>
|
||||||
|
<arg name="purpose" type="uint" enum="zwp_text_input_v3.content_purpose"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="done">
|
||||||
|
<description summary="apply state">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="commit_string">
|
||||||
|
<description summary="commit string">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="text" type="string"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="set_preedit_string">
|
||||||
|
<description summary="pre-edit 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.
|
||||||
|
</description>
|
||||||
|
<arg name="text" type="string"/>
|
||||||
|
<arg name="cursor_begin" type="int"/>
|
||||||
|
<arg name="cursor_end" type="int"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="delete_surrounding_text">
|
||||||
|
<description summary="delete text">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="before_length" type="uint"/>
|
||||||
|
<arg name="after_length" type="uint"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="commit">
|
||||||
|
<description summary="apply state">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="get_input_popup_surface">
|
||||||
|
<description summary="create popup surface">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="id" type="new_id" interface="zwp_input_popup_surface_v2"/>
|
||||||
|
<arg name="surface" type="object" interface="wl_surface"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="grab_keyboard">
|
||||||
|
<description summary="grab hardware keyboard">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="keyboard" type="new_id"
|
||||||
|
interface="zwp_input_method_keyboard_grab_v2"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="unavailable">
|
||||||
|
<description summary="input method unavailable">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the text input">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwp_input_popup_surface_v2" version="1">
|
||||||
|
<description summary="popup surface">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<event name="text_input_rectangle">
|
||||||
|
<description summary="set text input area position">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="x" type="int"/>
|
||||||
|
<arg name="y" type="int"/>
|
||||||
|
<arg name="width" type="int"/>
|
||||||
|
<arg name="height" type="int"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor"/>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwp_input_method_keyboard_grab_v2" version="1">
|
||||||
|
<!-- Closely follows wl_keyboard version 6 -->
|
||||||
|
<description summary="keyboard grab">
|
||||||
|
The zwp_input_method_keyboard_grab_v2 interface represents an exclusive
|
||||||
|
grab of the wl_keyboard interface associated with the seat.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<event name="keymap">
|
||||||
|
<description summary="keyboard mapping">
|
||||||
|
This event provides a file descriptor to the client which can be
|
||||||
|
memory-mapped to provide a keyboard mapping description.
|
||||||
|
</description>
|
||||||
|
<arg name="format" type="uint" enum="wl_keyboard.keymap_format"
|
||||||
|
summary="keymap format"/>
|
||||||
|
<arg name="fd" type="fd" summary="keymap file descriptor"/>
|
||||||
|
<arg name="size" type="uint" summary="keymap size, in bytes"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="key">
|
||||||
|
<description summary="key event">
|
||||||
|
A key was pressed or released.
|
||||||
|
The time argument is a timestamp with millisecond granularity, with an
|
||||||
|
undefined base.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint" summary="serial number of the key event"/>
|
||||||
|
<arg name="time" type="uint" summary="timestamp with millisecond granularity"/>
|
||||||
|
<arg name="key" type="uint" summary="key that produced the event"/>
|
||||||
|
<arg name="state" type="uint" enum="wl_keyboard.key_state"
|
||||||
|
summary="physical state of the key"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<event name="modifiers">
|
||||||
|
<description summary="modifier and group state">
|
||||||
|
Notifies clients that the modifier and/or group state has changed, and
|
||||||
|
it should update its local state.
|
||||||
|
</description>
|
||||||
|
<arg name="serial" type="uint" summary="serial number of the modifiers event"/>
|
||||||
|
<arg name="mods_depressed" type="uint" summary="depressed modifiers"/>
|
||||||
|
<arg name="mods_latched" type="uint" summary="latched modifiers"/>
|
||||||
|
<arg name="mods_locked" type="uint" summary="locked modifiers"/>
|
||||||
|
<arg name="group" type="uint" summary="keyboard layout"/>
|
||||||
|
</event>
|
||||||
|
|
||||||
|
<request name="release" type="destructor">
|
||||||
|
<description summary="release the grab object"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<event name="repeat_info">
|
||||||
|
<description summary="repeat rate and delay">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
<arg name="rate" type="int"
|
||||||
|
summary="the rate of repeating keys in characters per second"/>
|
||||||
|
<arg name="delay" type="int"
|
||||||
|
summary="delay in milliseconds since key down until repeating starts"/>
|
||||||
|
</event>
|
||||||
|
</interface>
|
||||||
|
|
||||||
|
<interface name="zwp_input_method_manager_v2" version="1">
|
||||||
|
<description summary="input method manager">
|
||||||
|
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.
|
||||||
|
</description>
|
||||||
|
|
||||||
|
<request name="get_input_method">
|
||||||
|
<description summary="request an input method object">
|
||||||
|
Request a new input zwp_input_method_v2 object associated with a given
|
||||||
|
seat.
|
||||||
|
</description>
|
||||||
|
<arg name="seat" type="object" interface="wl_seat"/>
|
||||||
|
<arg name="input_method" type="new_id" interface="zwp_input_method_v2"/>
|
||||||
|
</request>
|
||||||
|
|
||||||
|
<request name="destroy" type="destructor">
|
||||||
|
<description summary="destroy the input method manager">
|
||||||
|
Destroys the zwp_input_method_manager_v2 object.
|
||||||
|
|
||||||
|
The zwp_input_method_v2 objects originating from it remain valid.
|
||||||
|
</description>
|
||||||
|
</request>
|
||||||
|
</interface>
|
||||||
|
</protocol>
|
|
@ -38,6 +38,7 @@ new_protocols = [
|
||||||
['wlr-gamma-control-unstable-v1.xml'],
|
['wlr-gamma-control-unstable-v1.xml'],
|
||||||
['wlr-foreign-toplevel-management-unstable-v1.xml'],
|
['wlr-foreign-toplevel-management-unstable-v1.xml'],
|
||||||
['wlr-output-power-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/tearing-control/tearing-control-v1.xml'],
|
||||||
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
|
[wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'],
|
||||||
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
[wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'],
|
||||||
|
|
|
@ -262,8 +262,6 @@ void CCompositor::initServer() {
|
||||||
wlr_xdg_foreign_v1_create(m_sWLDisplay, m_sWLRForeignRegistry);
|
wlr_xdg_foreign_v1_create(m_sWLDisplay, m_sWLRForeignRegistry);
|
||||||
wlr_xdg_foreign_v2_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);
|
m_sWLRHeadlessBackend = wlr_headless_backend_create(m_sWLEventLoop);
|
||||||
|
|
||||||
if (!m_sWLRHeadlessBackend) {
|
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_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_sWLRVKeyboardMgr->events.new_virtual_keyboard, &Events::listen_newVirtualKeyboard, m_sWLRVKeyboardMgr, "VKeyboardMgr");
|
||||||
addWLSignal(&m_sWLRRenderer->events.destroy, &Events::listen_RendererDestroy, m_sWLRRenderer, "WLRRenderer");
|
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)
|
if (m_sWRLDRMLeaseMgr)
|
||||||
addWLSignal(&m_sWRLDRMLeaseMgr->events.request, &Events::listen_leaseRequest, &m_sWRLDRMLeaseMgr, "DRM");
|
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_newVirtPtr);
|
||||||
removeWLSignal(&Events::listen_newVirtualKeyboard);
|
removeWLSignal(&Events::listen_newVirtualKeyboard);
|
||||||
removeWLSignal(&Events::listen_RendererDestroy);
|
removeWLSignal(&Events::listen_RendererDestroy);
|
||||||
removeWLSignal(&Events::listen_newIME);
|
|
||||||
|
|
||||||
if (m_sWRLDRMLeaseMgr)
|
if (m_sWRLDRMLeaseMgr)
|
||||||
removeWLSignal(&Events::listen_leaseRequest);
|
removeWLSignal(&Events::listen_leaseRequest);
|
||||||
|
|
|
@ -64,7 +64,6 @@ class CCompositor {
|
||||||
wlr_virtual_pointer_manager_v1* m_sWLRVirtPtrMgr;
|
wlr_virtual_pointer_manager_v1* m_sWLRVirtPtrMgr;
|
||||||
wlr_tablet_manager_v2* m_sWLRTabletManager;
|
wlr_tablet_manager_v2* m_sWLRTabletManager;
|
||||||
wlr_xdg_foreign_registry* m_sWLRForeignRegistry;
|
wlr_xdg_foreign_registry* m_sWLRForeignRegistry;
|
||||||
wlr_input_method_manager_v2* m_sWLRIMEMgr;
|
|
||||||
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
|
wlr_linux_dmabuf_v1* m_sWLRLinuxDMABuf;
|
||||||
wlr_backend* m_sWLRHeadlessBackend;
|
wlr_backend* m_sWLRHeadlessBackend;
|
||||||
// ------------------------------------------------- //
|
// ------------------------------------------------- //
|
||||||
|
|
|
@ -107,7 +107,6 @@ namespace Events {
|
||||||
LISTENER(pinchEnd);
|
LISTENER(pinchEnd);
|
||||||
|
|
||||||
// IME
|
// IME
|
||||||
LISTENER(newIME);
|
|
||||||
LISTENER(newVirtualKeyboard);
|
LISTENER(newVirtualKeyboard);
|
||||||
|
|
||||||
// Touch
|
// Touch
|
||||||
|
|
|
@ -187,9 +187,3 @@ void Events::listener_sessionActive(wl_listener* listener, void* data) {
|
||||||
|
|
||||||
g_pConfigManager->m_bWantsMonitorReload = true;
|
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);
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
#include "../defines.hpp"
|
#include "../defines.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include "../Compositor.hpp"
|
#include "../Compositor.hpp"
|
||||||
|
#include "../managers/TokenManager.hpp"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#ifdef HAS_EXECINFO
|
#ifdef HAS_EXECINFO
|
||||||
|
@ -834,3 +837,35 @@ bool envEnabled(const std::string& env) {
|
||||||
return false;
|
return false;
|
||||||
return std::string(ENV) == "1";
|
return std::string(ENV) == "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<int, std::string> 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;
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ void throwError(const std::string& err);
|
||||||
uint32_t drmFormatToGL(uint32_t drm);
|
uint32_t drmFormatToGL(uint32_t drm);
|
||||||
uint32_t glFormatToType(uint32_t gl);
|
uint32_t glFormatToType(uint32_t gl);
|
||||||
bool envEnabled(const std::string& env);
|
bool envEnabled(const std::string& env);
|
||||||
|
int allocateSHMFile(size_t len);
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {
|
[[deprecated("use std::format instead")]] std::string getFormat(std::format_string<Args...> fmt, Args&&... args) {
|
||||||
|
|
|
@ -218,14 +218,6 @@ struct SSwipeGesture {
|
||||||
CMonitor* pMonitor = nullptr;
|
CMonitor* pMonitor = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SIMEKbGrab {
|
|
||||||
wlr_input_method_keyboard_grab_v2* pWlrKbGrab = nullptr;
|
|
||||||
|
|
||||||
wlr_keyboard* pKeyboard = nullptr;
|
|
||||||
|
|
||||||
DYNLISTENER(grabDestroy);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct STouchDevice {
|
struct STouchDevice {
|
||||||
wlr_input_device* pWlrDevice = nullptr;
|
wlr_input_device* pWlrDevice = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -83,7 +83,6 @@ extern "C" {
|
||||||
#include <wlr/types/wlr_xdg_foreign_registry.h>
|
#include <wlr/types/wlr_xdg_foreign_registry.h>
|
||||||
#include <wlr/types/wlr_xdg_foreign_v1.h>
|
#include <wlr/types/wlr_xdg_foreign_v1.h>
|
||||||
#include <wlr/types/wlr_xdg_foreign_v2.h>
|
#include <wlr/types/wlr_xdg_foreign_v2.h>
|
||||||
#include <wlr/types/wlr_input_method_v2.h>
|
|
||||||
#include <wlr/types/wlr_touch.h>
|
#include <wlr/types/wlr_touch.h>
|
||||||
#include <wlr/types/wlr_switch.h>
|
#include <wlr/types/wlr_switch.h>
|
||||||
#include <wlr/config.h>
|
#include <wlr/config.h>
|
||||||
|
|
|
@ -324,9 +324,6 @@ bool CKeybindManager::onKeyEvent(wlr_keyboard_key_event* e, SKeyboard* pKeyboard
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pKeyboard->isVirtual && g_pInputManager->shouldIgnoreVirtualKeyboard(pKeyboard))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!m_pXKBTranslationState) {
|
if (!m_pXKBTranslationState) {
|
||||||
Debug::log(ERR, "BUG THIS: m_pXKBTranslationState NULL!");
|
Debug::log(ERR, "BUG THIS: m_pXKBTranslationState NULL!");
|
||||||
updateXKBTranslationState();
|
updateXKBTranslationState();
|
||||||
|
|
|
@ -19,26 +19,7 @@
|
||||||
#include "../protocols/XDGActivation.hpp"
|
#include "../protocols/XDGActivation.hpp"
|
||||||
#include "../protocols/IdleNotify.hpp"
|
#include "../protocols/IdleNotify.hpp"
|
||||||
#include "../protocols/SessionLock.hpp"
|
#include "../protocols/SessionLock.hpp"
|
||||||
|
#include "../protocols/InputMethodV2.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"
|
|
||||||
|
|
||||||
CProtocolManager::CProtocolManager() {
|
CProtocolManager::CProtocolManager() {
|
||||||
|
|
||||||
|
@ -61,6 +42,7 @@ CProtocolManager::CProtocolManager() {
|
||||||
PROTO::activation = std::make_unique<CXDGActivationProtocol>(&xdg_activation_v1_interface, 1, "XDGActivation");
|
PROTO::activation = std::make_unique<CXDGActivationProtocol>(&xdg_activation_v1_interface, 1, "XDGActivation");
|
||||||
PROTO::idle = std::make_unique<CIdleNotifyProtocol>(&ext_idle_notifier_v1_interface, 1, "IdleNotify");
|
PROTO::idle = std::make_unique<CIdleNotifyProtocol>(&ext_idle_notifier_v1_interface, 1, "IdleNotify");
|
||||||
PROTO::sessionLock = std::make_unique<CSessionLockProtocol>(&ext_session_lock_manager_v1_interface, 1, "SessionLock");
|
PROTO::sessionLock = std::make_unique<CSessionLockProtocol>(&ext_session_lock_manager_v1_interface, 1, "SessionLock");
|
||||||
|
PROTO::ime = std::make_unique<CInputMethodV2Protocol>(&zwp_input_method_manager_v2_interface, 1, "IMEv2");
|
||||||
|
|
||||||
// Old protocol implementations.
|
// Old protocol implementations.
|
||||||
// TODO: rewrite them to use hyprwayland-scanner.
|
// TODO: rewrite them to use hyprwayland-scanner.
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "../../protocols/PointerConstraints.hpp"
|
#include "../../protocols/PointerConstraints.hpp"
|
||||||
#include "../../protocols/IdleNotify.hpp"
|
#include "../../protocols/IdleNotify.hpp"
|
||||||
#include "../../protocols/SessionLock.hpp"
|
#include "../../protocols/SessionLock.hpp"
|
||||||
|
#include "../../protocols/InputMethodV2.hpp"
|
||||||
|
|
||||||
CInputManager::CInputManager() {
|
CInputManager::CInputManager() {
|
||||||
m_sListeners.setCursorShape = PROTO::cursorShape->events.setShape.registerListener([this](std::any data) {
|
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)
|
if (!pKeyboard->enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const bool DISALLOWACTION = pKeyboard->isVirtual && shouldIgnoreVirtualKeyboard(pKeyboard);
|
||||||
|
|
||||||
const auto EMAP = std::unordered_map<std::string, std::any>{{"keyboard", pKeyboard}, {"event", e}};
|
const auto EMAP = std::unordered_map<std::string, std::any>{{"keyboard", pKeyboard}, {"event", e}};
|
||||||
EMIT_HOOK_EVENT_CANCELLABLE("keyPress", EMAP);
|
EMIT_HOOK_EVENT_CANCELLABLE("keyPress", EMAP);
|
||||||
|
|
||||||
|
@ -1216,17 +1219,16 @@ void CInputManager::onKeyboardKey(wlr_keyboard_key_event* e, SKeyboard* pKeyboar
|
||||||
g_pKeybindManager->dpms("on");
|
g_pKeybindManager->dpms("on");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool passEvent = g_pKeybindManager->onKeyEvent(e, pKeyboard);
|
bool passEvent = DISALLOWACTION || g_pKeybindManager->onKeyEvent(e, pKeyboard);
|
||||||
|
|
||||||
PROTO::idle->onActivity();
|
PROTO::idle->onActivity();
|
||||||
|
|
||||||
if (passEvent) {
|
if (passEvent) {
|
||||||
|
const auto IME = m_sIMERelay.m_pIME.lock();
|
||||||
|
|
||||||
const auto PIMEGRAB = m_sIMERelay.getIMEKeyboardGrab(pKeyboard);
|
if (IME && IME->hasGrab() && !DISALLOWACTION) {
|
||||||
|
IME->setKeyboard(wlr_keyboard_from_input_device(pKeyboard->keyboard));
|
||||||
if (PIMEGRAB && PIMEGRAB->pWlrKbGrab && PIMEGRAB->pWlrKbGrab->input_method) {
|
IME->sendKey(e->time_msec, e->keycode, e->state);
|
||||||
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);
|
|
||||||
} else {
|
} else {
|
||||||
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(pKeyboard->keyboard));
|
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);
|
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)
|
if (!pKeyboard->enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto PIMEGRAB = m_sIMERelay.getIMEKeyboardGrab(pKeyboard);
|
const bool DISALLOWACTION = pKeyboard->isVirtual && shouldIgnoreVirtualKeyboard(pKeyboard);
|
||||||
|
|
||||||
const auto ALLMODS = accumulateModsFromAllKBs();
|
const auto ALLMODS = accumulateModsFromAllKBs();
|
||||||
|
|
||||||
auto MODS = wlr_keyboard_from_input_device(pKeyboard->keyboard)->modifiers;
|
auto MODS = wlr_keyboard_from_input_device(pKeyboard->keyboard)->modifiers;
|
||||||
MODS.depressed = ALLMODS;
|
MODS.depressed = ALLMODS;
|
||||||
|
|
||||||
if (PIMEGRAB && PIMEGRAB->pWlrKbGrab && PIMEGRAB->pWlrKbGrab->input_method) {
|
const auto IME = m_sIMERelay.m_pIME.lock();
|
||||||
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);
|
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 {
|
} else {
|
||||||
wlr_seat_set_keyboard(g_pCompositor->m_sSeat.seat, wlr_keyboard_from_input_device(pKeyboard->keyboard));
|
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);
|
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) {
|
bool CInputManager::shouldIgnoreVirtualKeyboard(SKeyboard* pKeyboard) {
|
||||||
return !pKeyboard ||
|
return !pKeyboard ||
|
||||||
(m_sIMERelay.m_pKeyboardGrab &&
|
(!m_sIMERelay.m_pIME.expired() && m_sIMERelay.m_pIME.lock()->grabClient() == wl_resource_get_client(wlr_input_device_get_virtual_keyboard(pKeyboard->keyboard)->resource));
|
||||||
wl_resource_get_client(m_sIMERelay.m_pKeyboardGrab->pWlrKbGrab->resource) == wl_resource_get_client(wlr_input_device_get_virtual_keyboard(pKeyboard->keyboard)->resource));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputManager::refocus() {
|
void CInputManager::refocus() {
|
||||||
|
|
|
@ -2,37 +2,14 @@
|
||||||
#include "InputManager.hpp"
|
#include "InputManager.hpp"
|
||||||
#include "../../Compositor.hpp"
|
#include "../../Compositor.hpp"
|
||||||
#include "../../protocols/FractionalScale.hpp"
|
#include "../../protocols/FractionalScale.hpp"
|
||||||
|
#include "../../protocols/InputMethodV2.hpp"
|
||||||
|
|
||||||
CInputPopup::CInputPopup(wlr_input_popup_surface_v2* surf) : pWlr(surf) {
|
CInputPopup::CInputPopup(SP<CInputMethodPopupV2> popup_) : popup(popup_) {
|
||||||
surface.assign(surf->surface);
|
listeners.commit = popup_->events.commit.registerListener([this](std::any d) { onCommit(); });
|
||||||
initCallbacks();
|
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(); });
|
||||||
static void onCommit(void* owner, void* data) {
|
surface.assign(popup_->surface());
|
||||||
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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CWLSurface* CInputPopup::queryOwner() {
|
CWLSurface* CInputPopup::queryOwner() {
|
||||||
|
@ -45,11 +22,6 @@ CWLSurface* CInputPopup::queryOwner() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputPopup::onDestroy() {
|
void CInputPopup::onDestroy() {
|
||||||
hyprListener_commitPopup.removeCallback();
|
|
||||||
hyprListener_destroyPopup.removeCallback();
|
|
||||||
hyprListener_mapPopup.removeCallback();
|
|
||||||
hyprListener_unmapPopup.removeCallback();
|
|
||||||
|
|
||||||
g_pInputManager->m_sIMERelay.removePopup(this);
|
g_pInputManager->m_sIMERelay.removePopup(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +73,7 @@ void CInputPopup::damageSurface() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputPopup::updateBox() {
|
void CInputPopup::updateBox() {
|
||||||
if (!surface.wlr()->mapped)
|
if (!popup.lock()->mapped)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto OWNER = queryOwner();
|
const auto OWNER = queryOwner();
|
||||||
|
@ -142,7 +114,7 @@ void CInputPopup::updateBox() {
|
||||||
popupOffset.x -= popupOverflow;
|
popupOffset.x -= popupOverflow;
|
||||||
|
|
||||||
CBox cursorBoxLocal({-popupOffset.x, -popupOffset.y}, cursorBoxParent.size());
|
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);
|
CBox popupBoxParent(cursorBoxParent.pos() + popupOffset, currentPopupSize);
|
||||||
if (popupBoxParent != lastBoxLocal) {
|
if (popupBoxParent != lastBoxLocal) {
|
||||||
|
|
|
@ -4,17 +4,13 @@
|
||||||
#include "../../desktop/WLSurface.hpp"
|
#include "../../desktop/WLSurface.hpp"
|
||||||
#include "../../macros.hpp"
|
#include "../../macros.hpp"
|
||||||
#include "../../helpers/Box.hpp"
|
#include "../../helpers/Box.hpp"
|
||||||
|
#include "../../helpers/signal/Listener.hpp"
|
||||||
|
|
||||||
struct wlr_input_popup_surface_v2;
|
class CInputMethodPopupV2;
|
||||||
|
|
||||||
class CInputPopup {
|
class CInputPopup {
|
||||||
public:
|
public:
|
||||||
CInputPopup(wlr_input_popup_surface_v2* surf);
|
CInputPopup(SP<CInputMethodPopupV2> popup);
|
||||||
|
|
||||||
void onDestroy();
|
|
||||||
void onMap();
|
|
||||||
void onUnmap();
|
|
||||||
void onCommit();
|
|
||||||
|
|
||||||
void damageEntire();
|
void damageEntire();
|
||||||
void damageSurface();
|
void damageSurface();
|
||||||
|
@ -24,18 +20,25 @@ class CInputPopup {
|
||||||
CBox globalBox();
|
CBox globalBox();
|
||||||
wlr_surface* getWlrSurface();
|
wlr_surface* getWlrSurface();
|
||||||
|
|
||||||
|
void onCommit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initCallbacks();
|
CWLSurface* queryOwner();
|
||||||
CWLSurface* queryOwner();
|
void updateBox();
|
||||||
void updateBox();
|
|
||||||
|
|
||||||
wlr_input_popup_surface_v2* pWlr = nullptr;
|
void onDestroy();
|
||||||
CWLSurface surface;
|
void onMap();
|
||||||
CBox lastBoxLocal;
|
void onUnmap();
|
||||||
uint64_t lastMonitor = -1;
|
|
||||||
|
|
||||||
DYNLISTENER(mapPopup);
|
WP<CInputMethodPopupV2> popup;
|
||||||
DYNLISTENER(unmapPopup);
|
CWLSurface surface;
|
||||||
DYNLISTENER(destroyPopup);
|
CBox lastBoxLocal;
|
||||||
DYNLISTENER(commitPopup);
|
uint64_t lastMonitor = -1;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
CHyprSignalListener map;
|
||||||
|
CHyprSignalListener unmap;
|
||||||
|
CHyprSignalListener destroy;
|
||||||
|
CHyprSignalListener commit;
|
||||||
|
} listeners;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,98 +2,53 @@
|
||||||
#include "InputManager.hpp"
|
#include "InputManager.hpp"
|
||||||
#include "../../Compositor.hpp"
|
#include "../../Compositor.hpp"
|
||||||
#include "../../protocols/TextInputV3.hpp"
|
#include "../../protocols/TextInputV3.hpp"
|
||||||
|
#include "../../protocols/InputMethodV2.hpp"
|
||||||
|
|
||||||
CInputMethodRelay::CInputMethodRelay() {
|
CInputMethodRelay::CInputMethodRelay() {
|
||||||
static auto P = g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast<wlr_surface*>(param)); });
|
static auto P = g_pHookSystem->hookDynamic("keyboardFocus", [&](void* self, SCallbackInfo& info, std::any param) { onKeyboardFocus(std::any_cast<wlr_surface*>(param)); });
|
||||||
|
|
||||||
listeners.newTIV3 = PROTO::textInputV3->events.newTextInput.registerListener([this](std::any ti) { onNewTextInput(ti); });
|
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<SP<CInputMethodV2>>(ime)); });
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputMethodRelay::onNewIME(wlr_input_method_v2* pIME) {
|
void CInputMethodRelay::onNewIME(SP<CInputMethodV2> pIME) {
|
||||||
if (m_pWLRIME) {
|
if (!m_pIME.expired()) {
|
||||||
Debug::log(ERR, "Cannot register 2 IMEs at once!");
|
Debug::log(ERR, "Cannot register 2 IMEs at once!");
|
||||||
|
|
||||||
wlr_input_method_v2_send_unavailable(pIME);
|
pIME->unavailable();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_pWLRIME = pIME;
|
m_pIME = pIME;
|
||||||
|
|
||||||
hyprListener_IMECommit.initCallback(
|
listeners.commitIME = pIME->events.onCommit.registerListener([this](std::any d) {
|
||||||
&m_pWLRIME->events.commit,
|
const auto PTI = getFocusedTextInput();
|
||||||
[&](void* owner, void* data) {
|
|
||||||
const auto PTI = getFocusedTextInput();
|
|
||||||
const auto PIMR = (CInputMethodRelay*)owner;
|
|
||||||
|
|
||||||
if (!PTI) {
|
if (!PTI) {
|
||||||
Debug::log(LOG, "No focused TextInput on IME Commit");
|
Debug::log(LOG, "No focused TextInput on IME Commit");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PTI->updateIMEState(PIMR->m_pWLRIME);
|
PTI->updateIMEState(m_pIME.lock());
|
||||||
},
|
});
|
||||||
this, "IMERelay");
|
|
||||||
|
|
||||||
hyprListener_IMEDestroy.initCallback(
|
listeners.destroyIME = pIME->events.destroy.registerListener([this](std::any d) {
|
||||||
&m_pWLRIME->events.destroy,
|
const auto PTI = getFocusedTextInput();
|
||||||
[&](void* owner, void* data) {
|
|
||||||
m_pWLRIME = nullptr;
|
|
||||||
|
|
||||||
hyprListener_IMEDestroy.removeCallback();
|
Debug::log(LOG, "IME Destroy");
|
||||||
hyprListener_IMECommit.removeCallback();
|
|
||||||
hyprListener_IMEGrab.removeCallback();
|
|
||||||
hyprListener_IMENewPopup.removeCallback();
|
|
||||||
|
|
||||||
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<CInputPopup>(std::any_cast<SP<CInputMethodPopupV2>>(d)));
|
||||||
|
|
||||||
if (PTI)
|
Debug::log(LOG, "New input popup");
|
||||||
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<SIMEKbGrab>();
|
|
||||||
|
|
||||||
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<CInputPopup>((wlr_input_popup_surface_v2*)data));
|
|
||||||
|
|
||||||
Debug::log(LOG, "New input popup");
|
|
||||||
},
|
|
||||||
this, "IMERelay");
|
|
||||||
|
|
||||||
if (!g_pCompositor->m_pLastFocus)
|
if (!g_pCompositor->m_pLastFocus)
|
||||||
return;
|
return;
|
||||||
|
@ -117,22 +72,6 @@ void CInputMethodRelay::removePopup(CInputPopup* pPopup) {
|
||||||
std::erase_if(m_vIMEPopups, [pPopup](const auto& other) { return other.get() == 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() {
|
CTextInput* CInputMethodRelay::getFocusedTextInput() {
|
||||||
if (!g_pCompositor->m_pLastFocus)
|
if (!g_pCompositor->m_pLastFocus)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -164,33 +103,30 @@ void CInputMethodRelay::updateAllPopups() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputMethodRelay::activateIME(CTextInput* pInput) {
|
void CInputMethodRelay::activateIME(CTextInput* pInput) {
|
||||||
if (!m_pWLRIME)
|
if (m_pIME.expired())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wlr_input_method_v2_send_activate(g_pInputManager->m_sIMERelay.m_pWLRIME);
|
m_pIME.lock()->activate();
|
||||||
commitIMEState(pInput);
|
commitIMEState(pInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputMethodRelay::deactivateIME(CTextInput* pInput) {
|
void CInputMethodRelay::deactivateIME(CTextInput* pInput) {
|
||||||
if (!m_pWLRIME)
|
if (m_pIME.expired())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!m_pWLRIME->active)
|
m_pIME.lock()->deactivate();
|
||||||
return;
|
|
||||||
|
|
||||||
wlr_input_method_v2_send_deactivate(g_pInputManager->m_sIMERelay.m_pWLRIME);
|
|
||||||
commitIMEState(pInput);
|
commitIMEState(pInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputMethodRelay::commitIMEState(CTextInput* pInput) {
|
void CInputMethodRelay::commitIMEState(CTextInput* pInput) {
|
||||||
if (!m_pWLRIME)
|
if (m_pIME.expired())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pInput->commitStateToIME(m_pWLRIME);
|
pInput->commitStateToIME(m_pIME.lock());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) {
|
void CInputMethodRelay::onKeyboardFocus(wlr_surface* pSurface) {
|
||||||
if (!m_pWLRIME)
|
if (m_pIME.expired())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (pSurface == m_pLastKbFocus)
|
if (pSurface == m_pLastKbFocus)
|
||||||
|
|
|
@ -11,39 +11,36 @@
|
||||||
class CInputManager;
|
class CInputManager;
|
||||||
class CHyprRenderer;
|
class CHyprRenderer;
|
||||||
struct STextInputV1;
|
struct STextInputV1;
|
||||||
|
class CInputMethodV2;
|
||||||
|
|
||||||
class CInputMethodRelay {
|
class CInputMethodRelay {
|
||||||
public:
|
public:
|
||||||
CInputMethodRelay();
|
CInputMethodRelay();
|
||||||
|
|
||||||
void onNewIME(wlr_input_method_v2*);
|
void onNewIME(SP<CInputMethodV2>);
|
||||||
void onNewTextInput(std::any tiv3);
|
void onNewTextInput(std::any tiv3);
|
||||||
void onNewTextInput(STextInputV1* pTIV1);
|
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 onKeyboardFocus(wlr_surface*);
|
||||||
void deactivateIME(CTextInput* pInput);
|
|
||||||
void commitIMEState(CTextInput* pInput);
|
|
||||||
void removeTextInput(CTextInput* pInput);
|
|
||||||
|
|
||||||
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 updateAllPopups();
|
||||||
void removePopup(CInputPopup*);
|
|
||||||
|
|
||||||
CInputPopup* popupFromCoords(const Vector2D& point);
|
WP<CInputMethodV2> m_pIME;
|
||||||
CInputPopup* popupFromSurface(const wlr_surface* surface);
|
|
||||||
|
|
||||||
void updateAllPopups();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<SIMEKbGrab> m_pKeyboardGrab;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<CTextInput>> m_vTextInputs;
|
std::vector<std::unique_ptr<CTextInput>> m_vTextInputs;
|
||||||
std::vector<std::unique_ptr<CInputPopup>> m_vIMEPopups;
|
std::vector<std::unique_ptr<CInputPopup>> m_vIMEPopups;
|
||||||
|
|
||||||
|
@ -51,14 +48,12 @@ class CInputMethodRelay {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
CHyprSignalListener newTIV3;
|
CHyprSignalListener newTIV3;
|
||||||
|
CHyprSignalListener newIME;
|
||||||
|
CHyprSignalListener commitIME;
|
||||||
|
CHyprSignalListener destroyIME;
|
||||||
|
CHyprSignalListener newPopup;
|
||||||
} listeners;
|
} listeners;
|
||||||
|
|
||||||
DYNLISTENER(textInputNew);
|
|
||||||
DYNLISTENER(IMECommit);
|
|
||||||
DYNLISTENER(IMEDestroy);
|
|
||||||
DYNLISTENER(IMEGrab);
|
|
||||||
DYNLISTENER(IMENewPopup);
|
|
||||||
|
|
||||||
friend class CHyprRenderer;
|
friend class CHyprRenderer;
|
||||||
friend class CInputManager;
|
friend class CInputManager;
|
||||||
friend class CTextInputV1ProtocolManager;
|
friend class CTextInputV1ProtocolManager;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "../../protocols/TextInputV1.hpp"
|
#include "../../protocols/TextInputV1.hpp"
|
||||||
#include "../../Compositor.hpp"
|
#include "../../Compositor.hpp"
|
||||||
#include "../../protocols/TextInputV3.hpp"
|
#include "../../protocols/TextInputV3.hpp"
|
||||||
|
#include "../../protocols/InputMethodV2.hpp"
|
||||||
|
|
||||||
CTextInput::CTextInput(STextInputV1* ti) : pV1Input(ti) {
|
CTextInput::CTextInput(STextInputV1* ti) : pV1Input(ti) {
|
||||||
ti->pTextInput = this;
|
ti->pTextInput = this;
|
||||||
|
@ -67,7 +68,7 @@ void CTextInput::initCallbacks() {
|
||||||
void CTextInput::onEnabled(wlr_surface* surfV1) {
|
void CTextInput::onEnabled(wlr_surface* surfV1) {
|
||||||
Debug::log(LOG, "TI ENABLE");
|
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!");
|
// Debug::log(WARN, "Enabling TextInput on no IME!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +86,7 @@ void CTextInput::onEnabled(wlr_surface* surfV1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTextInput::onDisabled() {
|
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!");
|
// Debug::log(WARN, "Disabling TextInput on no IME!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -103,7 +104,7 @@ void CTextInput::onDisabled() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTextInput::onCommit() {
|
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!");
|
// Debug::log(WARN, "Committing TextInput on no IME!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -218,67 +219,66 @@ wl_client* CTextInput::client() {
|
||||||
return isV3() ? pV3Input.lock()->client() : pV1Input->client;
|
return isV3() ? pV3Input.lock()->client() : pV1Input->client;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTextInput::commitStateToIME(wlr_input_method_v2* ime) {
|
void CTextInput::commitStateToIME(SP<CInputMethodV2> ime) {
|
||||||
if (isV3()) {
|
if (isV3()) {
|
||||||
const auto INPUT = pV3Input.lock();
|
const auto INPUT = pV3Input.lock();
|
||||||
|
|
||||||
if (INPUT->current.surrounding.updated)
|
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)
|
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 {
|
} else {
|
||||||
if (pV1Input->pendingSurrounding.isPending)
|
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)
|
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();
|
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<CInputMethodV2> ime) {
|
||||||
if (isV3()) {
|
if (isV3()) {
|
||||||
const auto INPUT = pV3Input.lock();
|
const auto INPUT = pV3Input.lock();
|
||||||
|
|
||||||
if (ime->current.preedit.text)
|
if (ime->current.preeditString.committed)
|
||||||
INPUT->preeditString(ime->current.preedit.text, ime->current.preedit.cursor_begin, ime->current.preedit.cursor_end);
|
INPUT->preeditString(ime->current.preeditString.string, ime->current.preeditString.begin, ime->current.preeditString.end);
|
||||||
|
|
||||||
if (ime->current.commit_text)
|
if (ime->current.committedString.committed)
|
||||||
INPUT->commitString(ime->current.commit_text);
|
INPUT->commitString(ime->current.committedString.string);
|
||||||
|
|
||||||
if (ime->current.delete_.before_length || ime->current.delete_.after_length)
|
if (ime->current.deleteSurrounding.committed)
|
||||||
INPUT->deleteSurroundingText(ime->current.delete_.before_length, ime->current.delete_.after_length);
|
INPUT->deleteSurroundingText(ime->current.deleteSurrounding.before, ime->current.deleteSurrounding.after);
|
||||||
|
|
||||||
INPUT->sendDone();
|
INPUT->sendDone();
|
||||||
} else {
|
} else {
|
||||||
if (ime->current.preedit.text) {
|
if (ime->current.preeditString.committed) {
|
||||||
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, std::string(ime->current.preedit.text).length(), ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
|
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.preedit.text, "");
|
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preeditString.string.c_str(), "");
|
||||||
} else {
|
} 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_styling(pV1Input->resourceImpl, 0, 0, ZWP_TEXT_INPUT_V1_PREEDIT_STYLE_HIGHLIGHT);
|
||||||
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, "", "");
|
zwp_text_input_v1_send_preedit_string(pV1Input->resourceImpl, pV1Input->serial, "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ime->current.commit_text) {
|
if (ime->current.committedString.committed)
|
||||||
zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.commit_text);
|
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) {
|
if (ime->current.deleteSurrounding.committed) {
|
||||||
zwp_text_input_v1_send_delete_surrounding_text(pV1Input->resourceImpl, std::string(ime->current.preedit.text).length() - ime->current.delete_.before_length,
|
zwp_text_input_v1_send_delete_surrounding_text(pV1Input->resourceImpl, std::string(ime->current.preeditString.string).length() - ime->current.deleteSurrounding.before,
|
||||||
ime->current.delete_.after_length + ime->current.delete_.before_length);
|
ime->current.deleteSurrounding.after + ime->current.deleteSurrounding.before);
|
||||||
|
|
||||||
if (ime->current.preedit.text)
|
if (ime->current.preeditString.committed)
|
||||||
zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preedit.text);
|
zwp_text_input_v1_send_commit_string(pV1Input->resourceImpl, pV1Input->serial, ime->current.preeditString.string.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ struct wl_client;
|
||||||
|
|
||||||
struct STextInputV1;
|
struct STextInputV1;
|
||||||
class CTextInputV3;
|
class CTextInputV3;
|
||||||
|
class CInputMethodV2;
|
||||||
|
|
||||||
class CTextInput {
|
class CTextInput {
|
||||||
public:
|
public:
|
||||||
|
@ -23,8 +24,8 @@ class CTextInput {
|
||||||
void leave();
|
void leave();
|
||||||
void tiV1Destroyed();
|
void tiV1Destroyed();
|
||||||
wl_client* client();
|
wl_client* client();
|
||||||
void commitStateToIME(wlr_input_method_v2* ime);
|
void commitStateToIME(SP<CInputMethodV2> ime);
|
||||||
void updateIMEState(wlr_input_method_v2* ime);
|
void updateIMEState(SP<CInputMethodV2> ime);
|
||||||
|
|
||||||
void onEnabled(wlr_surface* surfV1 = nullptr);
|
void onEnabled(wlr_surface* surfV1 = nullptr);
|
||||||
void onDisabled();
|
void onDisabled();
|
||||||
|
|
383
src/protocols/InputMethodV2.cpp
Normal file
383
src/protocols/InputMethodV2.cpp
Normal file
|
@ -0,0 +1,383 @@
|
||||||
|
#include "InputMethodV2.hpp"
|
||||||
|
#include "../Compositor.hpp"
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#define LOGM PROTO::ime->protoLog
|
||||||
|
|
||||||
|
CInputMethodKeyboardGrabV2::CInputMethodKeyboardGrabV2(SP<CZwpInputMethodKeyboardGrabV2> resource_, SP<CInputMethodV2> 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<CInputMethodV2> CInputMethodKeyboardGrabV2::getOwner() {
|
||||||
|
return owner.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_client* CInputMethodKeyboardGrabV2::client() {
|
||||||
|
return wl_resource_get_client(resource->resource());
|
||||||
|
}
|
||||||
|
|
||||||
|
CInputMethodPopupV2::CInputMethodPopupV2(SP<CZwpInputPopupSurfaceV2> resource_, SP<CInputMethodV2> 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<CZwpInputMethodV2> 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<CInputMethodPopupV2>(
|
||||||
|
std::make_shared<CZwpInputPopupSurfaceV2>(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<CInputMethodKeyboardGrabV2>(std::make_shared<CZwpInputMethodKeyboardGrabV2>(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<CZwpInputMethodManagerV2>(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<CInputMethodV2>(std::make_shared<CZwpInputMethodV2>(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);
|
||||||
|
}
|
160
src/protocols/InputMethodV2.hpp
Normal file
160
src/protocols/InputMethodV2.hpp
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
#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<CZwpInputMethodV2> 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<CZwpInputMethodV2> resource;
|
||||||
|
std::vector<WP<CInputMethodKeyboardGrabV2>> grabs;
|
||||||
|
std::vector<WP<CInputMethodPopupV2>> popups;
|
||||||
|
|
||||||
|
WP<CInputMethodV2> self;
|
||||||
|
|
||||||
|
bool active = false;
|
||||||
|
|
||||||
|
CBox inputRectangle;
|
||||||
|
|
||||||
|
friend class CInputMethodPopupV2;
|
||||||
|
friend class CInputMethodKeyboardGrabV2;
|
||||||
|
friend class CInputMethodV2Protocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CInputMethodKeyboardGrabV2 {
|
||||||
|
public:
|
||||||
|
CInputMethodKeyboardGrabV2(SP<CZwpInputMethodKeyboardGrabV2> resource_, SP<CInputMethodV2> owner_);
|
||||||
|
~CInputMethodKeyboardGrabV2();
|
||||||
|
|
||||||
|
bool good();
|
||||||
|
SP<CInputMethodV2> 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<CZwpInputMethodKeyboardGrabV2> resource;
|
||||||
|
WP<CInputMethodV2> owner;
|
||||||
|
|
||||||
|
wlr_keyboard* pLastKeyboard = nullptr; // READ-ONLY
|
||||||
|
};
|
||||||
|
|
||||||
|
class CInputMethodPopupV2 {
|
||||||
|
public:
|
||||||
|
CInputMethodPopupV2(SP<CZwpInputPopupSurfaceV2> resource_, SP<CInputMethodV2> 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<CZwpInputPopupSurfaceV2> resource;
|
||||||
|
WP<CInputMethodV2> 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<CInputMethodV2>
|
||||||
|
} 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<UP<CZwpInputMethodManagerV2>> m_vManagers;
|
||||||
|
std::vector<SP<CInputMethodV2>> m_vIMEs;
|
||||||
|
std::vector<SP<CInputMethodKeyboardGrabV2>> m_vGrabs;
|
||||||
|
std::vector<SP<CInputMethodPopupV2>> m_vPopups;
|
||||||
|
|
||||||
|
friend class CInputMethodPopupV2;
|
||||||
|
friend class CInputMethodKeyboardGrabV2;
|
||||||
|
friend class CInputMethodV2;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace PROTO {
|
||||||
|
inline UP<CInputMethodV2Protocol> ime;
|
||||||
|
};
|
Loading…
Reference in a new issue