11/*! This implements support for the experimental xx-input-method-v2 protocol.
22 * That protocol will hopefully become -v3 without changing the API at some point.
3+ *
4+ *
5+ * This is a low-level interface to the input method. It will generally not check if the client is allowed to issue a request in context, e.g. when the input method is inactive.
6+ *
7+ * It does handle some serials for the client, as well as it checks the validity of values for the current protocol version.
8+ *
9+ * The client is responsible for avoiding protocol errors.
310 */
411
512use crate :: compositor:: Surface ;
613use crate :: globals:: GlobalData ;
714
815use log:: { debug, warn} ;
916
10- use std:: collections:: HashMap ;
17+ use std:: collections:: { HashMap , HashSet } ;
1118use std:: num:: Wrapping ;
1219use std:: ops:: Deref ;
1320use std:: sync:: { Arc , Mutex , MutexGuard , Weak } ;
1421
22+ use crate :: reexports:: protocols_experimental:: text_input:: v3:: client:: xx_text_input_v3:: {
23+ Action , ChangeCause , ContentHint , ContentPurpose , SupportedFeatures ,
24+ } ;
1525use wayland_client:: globals:: { BindError , GlobalList } ;
1626use wayland_client:: protocol:: wl_seat:: WlSeat ;
1727use wayland_client:: protocol:: wl_surface;
1828use wayland_client:: WEnum ;
1929use wayland_client:: { Connection , Dispatch , Proxy , QueueHandle } ;
20- use wayland_protocols:: wp:: text_input:: zv3:: client:: zwp_text_input_v3:: {
21- ChangeCause , ContentHint , ContentPurpose ,
22- } ;
2330
24- use wayland_protocols_experimental :: input_method:: v1:: client as protocol;
31+ use crate :: reexports :: protocols_experimental :: input_method:: v1:: client as protocol;
2532
2633pub use protocol:: xx_input_method_v1:: XxInputMethodV1 ;
2734pub use protocol:: xx_input_popup_positioner_v1:: XxInputPopupPositionerV1 ;
@@ -59,7 +66,7 @@ impl InputMethodManager {
5966 where
6067 D : Dispatch < XxInputMethodManagerV2 , GlobalData > + ' static ,
6168 {
62- let manager = globals. bind ( qh, 2 ..=2 , GlobalData ) ?;
69+ let manager = globals. bind ( qh, 2 ..=3 , GlobalData ) ?;
6370 Ok ( Self { manager } )
6471 }
6572
@@ -210,6 +217,15 @@ impl InputMethod {
210217 self . input_method . delete_surrounding_text ( before_length, after_length)
211218 }
212219
220+ /// This method doesn't check if the action has been made available for this text input.
221+ pub fn perform_action ( & self , action : Action ) {
222+ self . input_method . perform_action ( action)
223+ }
224+
225+ pub fn move_cursor ( & self , cursor : i32 , anchor : i32 ) {
226+ self . input_method . move_cursor ( cursor, anchor)
227+ }
228+
213229 pub fn commit ( & self ) {
214230 let data = self . input_method . data :: < InputMethodData > ( ) . unwrap ( ) ;
215231 let inner = & data. inner . lock ( ) . unwrap ( ) ;
@@ -341,36 +357,54 @@ pub struct SurroundingText {
341357 pub anchor : u32 ,
342358}
343359
360+ /// Describes operations that can be performed on this input method.
361+ #[ non_exhaustive]
362+ // non exhaustive so that bumping protocol version and adding new ones
363+ // doesn't automatically break compat
364+ #[ derive( Clone , Debug , PartialEq ) ]
365+ pub struct Capabilities {
366+ pub surrounding_text : bool ,
367+ pub content_type : bool ,
368+ pub actions : HashSet < Action > ,
369+ pub supported_features : SupportedFeatures ,
370+ }
371+
372+ impl Default for Capabilities {
373+ fn default ( ) -> Self {
374+ Self {
375+ surrounding_text : false ,
376+ content_type : false ,
377+ actions : Default :: default ( ) ,
378+ supported_features : SupportedFeatures :: empty ( ) ,
379+ }
380+ }
381+ }
382+
344383/// State machine for determining the capabilities of a text input
345- #[ derive( Clone , Debug , Default , Copy , PartialEq ) ]
384+ #[ derive( Clone , Debug , Default , PartialEq ) ]
346385pub enum Active {
347386 #[ default]
348387 Inactive ,
349- NegotiatingCapabilities {
350- surrounding_text : bool ,
351- content_type : bool ,
352- } ,
353- Active {
354- surrounding_text : bool ,
355- content_type : bool ,
356- } ,
388+ NegotiatingCapabilities ( Capabilities ) ,
389+ Active ( Capabilities ) ,
357390}
358391
359392impl Active {
360393 fn with_active ( self ) -> Self {
361394 match self {
362- Self :: Inactive => {
363- Self :: NegotiatingCapabilities { content_type : false , surrounding_text : false }
364- }
395+ Self :: Inactive => Self :: NegotiatingCapabilities ( Capabilities :: default ( ) ) ,
365396 other => other,
366397 }
367398 }
368399
369400 fn with_surrounding_text ( self ) -> Self {
370401 match self {
371402 Self :: Inactive => Self :: Inactive ,
372- Self :: NegotiatingCapabilities { content_type, .. } => {
373- Self :: NegotiatingCapabilities { content_type, surrounding_text : true }
403+ Self :: NegotiatingCapabilities ( capabilities) => {
404+ Self :: NegotiatingCapabilities ( Capabilities {
405+ surrounding_text : true ,
406+ ..capabilities
407+ } )
374408 }
375409 active @ Self :: Active { .. } => active,
376410 }
@@ -379,22 +413,40 @@ impl Active {
379413 fn with_content_type ( self ) -> Self {
380414 match self {
381415 Self :: Inactive => Self :: Inactive ,
382- Self :: NegotiatingCapabilities { surrounding_text , .. } => {
383- Self :: NegotiatingCapabilities { content_type : true , surrounding_text }
416+ Self :: NegotiatingCapabilities ( capabilities ) => {
417+ Self :: NegotiatingCapabilities ( Capabilities { content_type : true , ..capabilities } )
384418 }
385419 active @ Self :: Active { .. } => active,
386420 }
387421 }
388422
389- fn with_done ( self ) -> Self {
423+ fn with_actions ( self , actions : HashSet < Action > ) -> Self {
424+ match self {
425+ Self :: Inactive => Self :: Inactive ,
426+ Self :: NegotiatingCapabilities ( capabilities) => {
427+ Self :: NegotiatingCapabilities ( Capabilities { actions, ..capabilities } )
428+ }
429+ active @ Self :: Active { .. } => active,
430+ }
431+ }
432+
433+ fn with_extra_features ( self , supported_features : SupportedFeatures ) -> Self {
390434 match self {
391435 Self :: Inactive => Self :: Inactive ,
392- Self :: NegotiatingCapabilities { surrounding_text , content_type } => {
393- Self :: Active { content_type , surrounding_text }
436+ Self :: NegotiatingCapabilities ( capabilities ) => {
437+ Self :: NegotiatingCapabilities ( Capabilities { supported_features , ..capabilities } )
394438 }
395439 active @ Self :: Active { .. } => active,
396440 }
397441 }
442+
443+ fn with_done ( self ) -> Self {
444+ match self {
445+ Self :: Inactive => Self :: Inactive ,
446+ Self :: NegotiatingCapabilities ( capabilities) => Self :: Active ( capabilities) ,
447+ active @ Self :: Active { .. } => active,
448+ }
449+ }
398450}
399451
400452#[ derive( Debug ) ]
@@ -602,7 +654,7 @@ where
602654 match event {
603655 Event :: Activate => {
604656 imdata. pending_state = InputMethodEventState {
605- active : imdata. pending_state . active . with_active ( ) ,
657+ active : imdata. pending_state . active . clone ( ) . with_active ( ) ,
606658 ..Default :: default ( )
607659 } ;
608660 }
@@ -611,7 +663,7 @@ where
611663 }
612664 Event :: SurroundingText { text, cursor, anchor } => {
613665 imdata. pending_state = InputMethodEventState {
614- active : imdata. pending_state . active . with_surrounding_text ( ) ,
666+ active : imdata. pending_state . active . clone ( ) . with_surrounding_text ( ) ,
615667 surrounding : SurroundingText { text, cursor, anchor } ,
616668 ..imdata. pending_state . clone ( )
617669 }
@@ -633,7 +685,7 @@ where
633685 }
634686 Event :: ContentType { hint, purpose } => {
635687 imdata. pending_state = InputMethodEventState {
636- active : imdata. pending_state . active . with_content_type ( ) ,
688+ active : imdata. pending_state . active . clone ( ) . with_content_type ( ) ,
637689 content_hint : match hint {
638690 WEnum :: Value ( hint) => hint,
639691 WEnum :: Unknown ( value) => {
@@ -655,9 +707,35 @@ where
655707 ..imdata. pending_state . clone ( )
656708 }
657709 }
710+ Event :: SetAvailableActions { available_actions } => {
711+ imdata. pending_state = InputMethodEventState {
712+ active : imdata. pending_state . active . clone ( ) . with_actions (
713+ HashSet :: from_iter ( available_actions. iter ( ) . filter_map ( |num| {
714+ Action :: try_from ( * num as u32 )
715+ . map_err ( |( ) | warn ! ( "Unknown available action {num}, ignoring" ) )
716+ . ok ( )
717+ } ) )
718+ ) ,
719+ ..imdata. pending_state . clone ( )
720+ }
721+ }
722+ Event :: AnnounceSupportedFeatures { features } => {
723+ imdata. pending_state = InputMethodEventState {
724+ active : imdata. pending_state . active . clone ( ) . with_extra_features (
725+ match features {
726+ WEnum :: Value ( v) => v,
727+ WEnum :: Unknown ( value) => {
728+ warn ! ( "Unknown `features`: {value}. Assuming no extra features supported." ) ;
729+ SupportedFeatures :: empty ( )
730+ }
731+ }
732+ ) ,
733+ ..imdata. pending_state . clone ( )
734+ }
735+ }
658736 Event :: Done => {
659737 imdata. pending_state = InputMethodEventState {
660- active : imdata. pending_state . active . with_done ( ) ,
738+ active : imdata. pending_state . active . clone ( ) . with_done ( ) ,
661739 ..imdata. pending_state . clone ( )
662740 } ;
663741 for ( popup, state) in imdata. pending_state . popups . iter_mut ( ) {
0 commit comments