Skip to content

Commit cc6688c

Browse files
committed
fixup! JBR-5672 Wayland: support input methods (temporary commit)
Adds two patches from the review branch: * 7ff4a82 * 0fbd1e3
1 parent 0d010e9 commit cc6688c

File tree

1 file changed

+51
-41
lines changed

1 file changed

+51
-41
lines changed

src/java.desktop/unix/classes/sun/awt/wl/im/text_input_unstable_v3/WLInputMethodZwpTextInputV3.java

Lines changed: 51 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
package sun.awt.wl.im.text_input_unstable_v3;
2727

2828
import sun.awt.AWTAccessor;
29+
import sun.awt.SunToolkit;
2930
import sun.awt.im.InputMethodAdapter;
3031
import sun.awt.wl.WLComponentPeer;
3132
import 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

Comments
 (0)