2626package sun .awt .wl .im .text_input_unstable_v3 ;
2727
2828import sun .awt .AWTAccessor ;
29+ import sun .awt .SunToolkit ;
2930import sun .awt .im .InputMethodAdapter ;
3031import sun .awt .wl .WLComponentPeer ;
3132import sun .util .logging .PlatformLogger ;
@@ -205,7 +206,7 @@ public void activate() {
205206
206207 if (getClientComponent () != getPreviousClientComponent ()) {
207208 // The if is required to make sure we don't accidentally lose a still actual preedit string
208- this .awtCurrentClientLatestDispatchedPreeditString = null ;
209+ this .awtCurrentClientLatestPostedPreeditString = null ;
209210 }
210211
211212 // It may be wrong to invoke this only if awtActivationStatus was DEACTIVATED.
@@ -270,7 +271,7 @@ public void endComposition() {
270271 }
271272
272273 // Deleting the current preedit text
273- awtDispatchIMESafely (JavaPreeditString .EMPTY , JavaCommitString .EMPTY );
274+ awtPostIMESafely (JavaPreeditString .EMPTY , JavaCommitString .EMPTY );
274275
275276 // Ending the composition on the native IM's side: the protocol doesn't provide a separate method for this,
276277 // but we can achieve this by disabling + enabling back the native context.
@@ -289,7 +290,7 @@ public void dispose() {
289290
290291 awtActivationStatus = AWTActivationStatus .DEACTIVATED ;
291292 awtNativeImIsExplicitlyDisabled = false ;
292- awtCurrentClientLatestDispatchedPreeditString = null ;
293+ awtCurrentClientLatestPostedPreeditString = null ;
293294 awtPreviousClientComponent = null ;
294295 awtClientComponentCaretPositionTracker .stopTrackingCurrentComponent ();
295296 wlDisposeContext ();
@@ -309,7 +310,7 @@ public String toString() {
309310 sb .append ("WLInputMethodZwpTextInputV3@" ).append (System .identityHashCode (this ));
310311 sb .append ('{' );
311312 sb .append ("awtActivationStatus=" ).append (awtActivationStatus );
312- sb .append (", awtCurrentClientLatestDispatchedPreeditString =" ).append (awtCurrentClientLatestDispatchedPreeditString );
313+ sb .append (", awtCurrentClientLatestPostedPreeditString =" ).append (awtCurrentClientLatestPostedPreeditString );
313314 sb .append (", wlInputContextState=" ).append (wlInputContextState );
314315 sb .append (", wlPendingChanges=" ).append (wlPendingChanges );
315316 sb .append (", wlBeingCommittedChanges=" ).append (wlBeingCommittedChanges );
@@ -357,10 +358,10 @@ private enum AWTActivationStatus {
357358 /** {@link #setInputMethodContext(InputMethodContext)} */
358359 private InputMethodContext awtImContext = null ;
359360 /**
360- * {@link #awtDispatchIMESafely (JavaPreeditString, JavaCommitString)}.
361- * {@code null} if no preedit strings have been dispatched since latest {@link #activate}.
361+ * {@link #awtPostIMESafely (JavaPreeditString, JavaCommitString)}.
362+ * {@code null} if no preedit strings have been posted since latest {@link #activate}.
362363 */
363- private JavaPreeditString awtCurrentClientLatestDispatchedPreeditString = null ;
364+ private JavaPreeditString awtCurrentClientLatestPostedPreeditString = null ;
364365 /**
365366 * Holds the value of {@link #getClientComponent()} since the latest {@link #deactivate(boolean)}
366367 * It allows to determine whether the focused component has actually changed between the latest deactivate-activate.
@@ -546,10 +547,14 @@ private Component getPreviousClientComponent() {
546547 }
547548
548549
549- /** @return {@code true} if a new InputMethodEvent has been successfully made and dispatched, {@code false} otherwise. */
550- private boolean awtDispatchIMESafely (JavaPreeditString preeditString , JavaCommitString commitString ) {
550+ // Although we wouldn't break any AWT/Swing rules,
551+ // we can't dispatch InputMethodEvents synchronously instead of posting them to the event queue.
552+ // This is because IntelliJ can't properly handle InputEvents that go around the event queue directly to a component.
553+ // See TODO: link to the ticket for more info.
554+ /** @return {@code true} if a new InputMethodEvent has been successfully made and posted, {@code false} otherwise. */
555+ private boolean awtPostIMESafely (JavaPreeditString preeditString , JavaCommitString commitString ) {
551556 if (log .isLoggable (PlatformLogger .Level .FINER )) {
552- log .finer ("awtDispatchIMESafely (preeditString={0}, commitString={1}): this={2}." , preeditString , commitString , this );
557+ log .finer ("awtPostIMESafely (preeditString={0}, commitString={1}): this={2}." , preeditString , commitString , this );
553558 }
554559
555560 assert (preeditString != null );
@@ -560,7 +565,7 @@ private boolean awtDispatchIMESafely(JavaPreeditString preeditString, JavaCommit
560565 // Supposing an input method shouldn't interact with UI when not activated.
561566
562567 if (log .isLoggable (PlatformLogger .Level .FINE )) {
563- log .fine ("awtDispatchIMESafely (preeditString={0}, commitString={1}): ignoring an attempt to dispatch a new InputMethodEvent from an inactive InputMethod={2}." ,
568+ log .fine ("awtPostIMESafely (preeditString={0}, commitString={1}): ignoring an attempt to post a new InputMethodEvent from an inactive InputMethod={2}." ,
564569 preeditString , commitString , this );
565570 }
566571
@@ -573,7 +578,7 @@ private boolean awtDispatchIMESafely(JavaPreeditString preeditString, JavaCommit
573578
574579 if (log .isLoggable (PlatformLogger .Level .WARNING )) {
575580 log .warning (
576- "awtDispatchIMESafely (preeditString={0}, commitString={1}): can''t dispatch a new InputMethodEvent because there''s no client component to dispatch to, although this InputMethod is active. this={2}." ,
581+ "awtPostIMESafely (preeditString={0}, commitString={1}): can''t post a new InputMethodEvent because there''s no client component to dispatch to, although this InputMethod is active. this={2}." ,
577582 preeditString , commitString , this
578583 );
579584 }
@@ -607,7 +612,7 @@ private boolean awtDispatchIMESafely(JavaPreeditString preeditString, JavaCommit
607612 }
608613 }
609614
610- final var clientCurrentPreeditText = Objects .requireNonNullElse (this .awtCurrentClientLatestDispatchedPreeditString , JavaPreeditString .EMPTY );
615+ final var clientCurrentPreeditText = Objects .requireNonNullElse (this .awtCurrentClientLatestPostedPreeditString , JavaPreeditString .EMPTY );
611616 if ( commitString .equals (JavaCommitString .EMPTY ) &&
612617 preeditString .equals (JavaPreeditString .EMPTY ) &&
613618 clientCurrentPreeditText .equals (JavaPreeditString .EMPTY ) )
@@ -623,8 +628,8 @@ private boolean awtDispatchIMESafely(JavaPreeditString preeditString, JavaCommit
623628 // text attributes and god knows what else.
624629
625630 if (log .isLoggable (PlatformLogger .Level .FINE )) {
626- log .fine ("awtDispatchIMESafely (preeditString={0}, commitString={1}): ignoring a redundant attempt to dispatch a new empty InputMethodEvent to a component with no preedit text. this={2}." ,
627- preeditString , commitString , this );
631+ log .fine ("awtPostIMESafely (preeditString={0}, commitString={1}): ignoring a redundant attempt to post a new empty InputMethodEvent to a component with no preedit text. this={2}." ,
632+ preeditString , commitString , this );
628633 }
629634
630635 return false ;
@@ -679,36 +684,29 @@ private boolean awtDispatchIMESafely(JavaPreeditString preeditString, JavaCommit
679684 }
680685 }
681686
682- if (log .isLoggable (PlatformLogger .Level .FINEST )) {
683- log .finest (
684- "awtDispatchIMESafely(...): dispatching an InputMethodEvent equal to {0}. this={1}." ,
685- // the parameters of this fictitious IME must be synchronized with what's actually dispatched below
686- new InputMethodEvent (
687- clientComponent ,
688- InputMethodEvent .INPUT_METHOD_TEXT_CHANGED ,
689- imeText .getIterator (),
690- commitString .text ().length (),
691- imeCaret ,
692- null
693- ),
694- this
695- );
696- }
697-
698- // NB: keep the parameters passed here synchronized with what's logged above.
699- this .awtImContext .dispatchInputMethodEvent (
687+ final InputMethodEvent ime = new InputMethodEvent (
688+ clientComponent ,
700689 InputMethodEvent .INPUT_METHOD_TEXT_CHANGED ,
701690 imeText .getIterator (),
702691 commitString .text ().length (),
703692 imeCaret ,
704693 // no recommendation for a visible position within the current preedit text
705694 null
706695 );
707- this .awtCurrentClientLatestDispatchedPreeditString = preeditString ;
696+
697+ if (log .isLoggable (PlatformLogger .Level .FINEST )) {
698+ log .finest (
699+ String .format ("awtPostIMESafely(...): posting a new InputMethodEvent=%s. this=%s." , ime , this ),
700+ new Throwable ("Stacktrace" )
701+ );
702+ }
703+
704+ SunToolkit .postEvent (SunToolkit .targetToAppContext (clientComponent ), ime );
705+ this .awtCurrentClientLatestPostedPreeditString = preeditString ;
708706
709707 return true ;
710708 } catch (Exception err ) {
711- log .severe ("Error occurred during constructing and dispatching a new InputMethodEvent." , err );
709+ log .severe ("Error occurred during constructing or posting a new InputMethodEvent." , err );
712710 }
713711
714712 return false ;
@@ -1130,10 +1128,10 @@ private void wlHandleContextGotDisabled() {
11301128 // Erasing preedit text from the client component
11311129 if (awtActivationStatus == AWTActivationStatus .ACTIVATED ) {
11321130 final boolean preeditIsEmpty =
1133- (awtCurrentClientLatestDispatchedPreeditString == null )
1134- || awtCurrentClientLatestDispatchedPreeditString .text ().isEmpty ();
1131+ (awtCurrentClientLatestPostedPreeditString == null )
1132+ || awtCurrentClientLatestPostedPreeditString .text ().isEmpty ();
11351133 if (!preeditIsEmpty ) {
1136- awtDispatchIMESafely (JavaPreeditString .EMPTY , JavaCommitString .EMPTY );
1134+ awtPostIMESafely (JavaPreeditString .EMPTY , JavaCommitString .EMPTY );
11371135 }
11381136 }
11391137 } catch (Exception err ) {
@@ -1437,7 +1435,7 @@ private void zwp_text_input_v3_onDone(long doneSerial) {
14371435 // +-------+----------------------------------------+ |
14381436 // 1| |
14391437 // v +-------+------------------------------------+
1440- // awtDispatchIMESafely (...) |zwp_text_input_v3::set_cursor_rectangle(...)|
1438+ // awtPostIMESafely (...) |zwp_text_input_v3::set_cursor_rectangle(...)|
14411439 // 2| | |
14421440 // v |zwp_text_input_v3::commit() |
14431441 // the component deletes its current composed text +-------^------------------------------------+
@@ -1479,12 +1477,24 @@ private void zwp_text_input_v3_onDone(long doneSerial) {
14791477 //
14801478 // TODO: unconditional dispatching of the text makes it impossible for the caret to leave the component's
14811479 // visible area, which would be useful for scrolling.
1482- awtDispatchIMESafely (preeditStringToApply , commitStringToApply );
1480+ awtPostIMESafely (preeditStringToApply , commitStringToApply );
14831481
14841482 if (doDeferCaretPositionUpdates ) {
14851483 // invokeLater is needed to handle a case when the client component decides to post the caret updates
14861484 // to the EventQueue instead of dispatching them right away (IDK if it happens in practice).
1487- EventQueue .invokeLater (() -> this .awtClientComponentCaretPositionTracker .resumeUpdates (false ));
1485+ // Nested invokeLater is needed because the IME event hasn't been dispatched yet (only posted).
1486+ // This way we guarantee that all UI activities associated with it are finished before resuming.
1487+ EventQueue .invokeLater (() -> EventQueue .invokeLater (() -> {
1488+ try {
1489+ this .awtClientComponentCaretPositionTracker .resumeUpdates (false );
1490+ } catch (Exception err ) {
1491+ log .severe (
1492+ String .format ("zwp_text_input_v3_onDone(doneSerial=%d): ClientComponentCaretPositionTracker.resumeUpdates() failed. this=%s." ,
1493+ doneSerial , this ),
1494+ err
1495+ );
1496+ }
1497+ }));
14881498 }
14891499
14901500 // Sending pending changes (if any)
0 commit comments